diff --git a/ide_plugins/vs/BeforeDeployCheck.txt b/ide_plugins/vs/BeforeDeployCheck.ckl similarity index 58% rename from ide_plugins/vs/BeforeDeployCheck.txt rename to ide_plugins/vs/BeforeDeployCheck.ckl index e2936cdd..d99dc7aa 100644 --- a/ide_plugins/vs/BeforeDeployCheck.txt +++ b/ide_plugins/vs/BeforeDeployCheck.ckl @@ -1,6 +1,11 @@ -Check latest plugin version is installed -Start VS and load a C/C++ solution (best mixed with multiple projects) -Check all projects are loaded in Solution Explorer +-Check Plugin Logging and Obfuscation is off + -VS Menu Bar -> Tools -> Options -> Coati -> Logging & Obfuscation is off +-Delete log file and dictionary for current VS session if present + -delete log file for current session at ..AppData/Local/Coati Software/Plugin/VS if present + -delete dictionary file for current session at ..AppData/Local/Coati Software/Plugin/VS if present -Create CDB from solution -Check all C/C++ projects listed in CDB dialogue -Check eventual non-C/C++ projects are not listed @@ -29,25 +34,47 @@ -Check finished CDB -check at correct location with correct name -check CDB file is non-empty (at least estimate whether number of lines in CDB coincides with VS solution) +-Check no new Log file or Dictionary file was created + -Check ..AppData/Local/Coati Software/Plugin/VS for no new log created + -Check ..AppData/Local/Coati Software/Plugin/VS for no new dictionary created +-Turn on Logging + -VS Menu Bar -> Tools -> Options -> Coati -> Logging ON & Obfuscation OFF +-Enable VS Output Window + -VS Menu Bar -> View -> Output + -In Output Window, show Coati log (Show Output from: Coati Log) -Create CDB from solution again -Check location and name is the same as last time -Check dialogue asking whether to overwrite old file or not -Click no -Check number appended to file name -Create CDB with the new name + -Check VS Output Window displays messages during creation + -Check project- and file names and directories are in clear text -Check finished CDB -check at correct location with correct name -check CDB file is non-empty (at least estimate whether number of lines in CDB coincides with VS solution) -Unload a project in the Solution Explorer +-Check new Log file but no Dictionary file was created + -Check ..AppData/Local/Coati Software/Plugin/VS for new log created + -Check ..AppData/Local/Coati Software/Plugin/VS for no new dictionary created +-Turn on Obfuscation + -VS Menu Bar -> Tools -> Options -> Coati -> Logging ON & Obfuscation ON -Create CDB from solution again -Check unloaded project is not in the listed projects in the dialogue -Check location and name is the same as first time -Check dialogue asking whether to overwrite old file or not -Click yes -Click Create + -Check VS Output Window displays messages during creation + -Check project- and file names and directories are obfuscated (a0, b0, c0,...) -Check finished CDB -check at correct location with correct name -check CDB file is smaller now (reduced size has to make sense considering the missing project) +-Check new Log entries and Dictionary file was created + -Check ..AppData/Local/Coati Software/Plugin/VS for no new log file + -Check the log file created in this session at ..AppData/Local/Coati Software/Plugin/VS for new entries + -Check new entries are using obfuscated names + -Check ..AppData/Local/Coati Software/Plugin/VS for new dictionary created -Create CDB from solution again -Check location and name is the same as first time -Check dialogue asking whether to overwrite old file or not diff --git a/ide_plugins/vs/TestSolutionEdgeCases.txt b/ide_plugins/vs/TestSolutionEdgeCases.txt index 2cfd83ba..b048dc5c 100644 --- a/ide_plugins/vs/TestSolutionEdgeCases.txt +++ b/ide_plugins/vs/TestSolutionEdgeCases.txt @@ -11,4 +11,9 @@ These Edge Cases should be considered in a dedicated Test Solution for the Plugi -at least two levels deep -Include Paths - -add at least one include path to Project->Properties->Configuration Properties->VC++ Directories->Include Directories \ No newline at end of file + -add at least one include path to Project->Properties->Configuration Properties->VC++ Directories->Include Directories + +-Solution Structure + -have a solution with no projects directly under the solution + -so: Solution/Folder/Project + -no: Solution/Project \ No newline at end of file diff --git a/ide_plugins/vs/coati_plugin_vs.vsix b/ide_plugins/vs/coati_plugin_vs.vsix index 772ac7d8..dd2e0e83 100644 Binary files a/ide_plugins/vs/coati_plugin_vs.vsix and b/ide_plugins/vs/coati_plugin_vs.vsix differ diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/CoatiPlugin.csproj b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/CoatiPlugin.csproj index 87289eca..2023db6f 100644 --- a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/CoatiPlugin.csproj +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/CoatiPlugin.csproj @@ -129,6 +129,13 @@ + + + + + + + @@ -194,7 +201,6 @@ - Designer @@ -217,6 +223,7 @@ true + true diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/CoatiPluginPackage.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/CoatiPluginPackage.cs index 95ae768b..e7a34d64 100644 --- a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/CoatiPluginPackage.cs +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/CoatiPluginPackage.cs @@ -16,10 +16,14 @@ namespace CoatiSoftware.CoatiPlugin { private uint _serverPort = 6666; private uint _clientPort = 6667; + private bool _logging = false; + private bool _obfuscateLogging = false; public delegate void Callback(); public static Callback _serverPortChangeCallback = null; public static Callback _clientPortChangeCallback = null; + public static Callback _loggingToggled = null; + public static Callback _obfuscationToggled = null; [Category("Coati")] [DisplayName("VS Port")] @@ -53,6 +57,38 @@ namespace CoatiSoftware.CoatiPlugin } } + [Category("Coati")] + [DisplayName("File Logging")] + [Description("Enables or disables file logging for the plugin")] + public bool LoggingEnabled + { + get { return _logging; } + set + { + _logging = value; + if(_loggingToggled != null) + { + _loggingToggled(); + } + } + } + + [Category("Coati")] + [DisplayName("Log Obfuscation")] + [Description("Names will be obfuscated in log files. Note that already logged data will not be obfuscated retroactively. A dictionary file will be created that you can use to make sense of obfuscated logs. Keep this dictionary to yourself!")] + public bool ObfuscateLogging + { + get { return _obfuscateLogging; } + set + { + _obfuscateLogging = value; + if (_obfuscationToggled != null) + { + _obfuscationToggled(); + } + } + } + public OptionPageGrid() { } @@ -91,6 +127,24 @@ namespace CoatiSoftware.CoatiPlugin } } + public bool LoggingEnabled + { + get + { + OptionPageGrid page = (OptionPageGrid)GetDialogPage(typeof(OptionPageGrid)); + return page.LoggingEnabled; + } + } + + public bool LogObfuscationEnabled + { + get + { + OptionPageGrid page = (OptionPageGrid)GetDialogPage(typeof(OptionPageGrid)); + return page.ObfuscateLogging; + } + } + System.Threading.Thread _serverThread = null; public CoatiPluginPackage() @@ -100,12 +154,15 @@ namespace CoatiSoftware.CoatiPlugin { base.Initialize(); + InitLogging(); InitNetwork(); Utility.FileUtility._errorCallback = new Utility.FileUtility.ErrorCallback(OnFileUtilityError); OptionPageGrid._serverPortChangeCallback = new OptionPageGrid.Callback(OnServerPortChanged); OptionPageGrid._clientPortChangeCallback = new OptionPageGrid.Callback(OnClientPortChanged); + OptionPageGrid._loggingToggled = new OptionPageGrid.Callback(OnLoggingToggled); + OptionPageGrid._obfuscationToggled = new OptionPageGrid.Callback(OnObfuscationToggled); OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService; if ( null != mcs ) @@ -133,10 +190,14 @@ namespace CoatiSoftware.CoatiPlugin _solutionEvents.Opened += OnSolutionOpened; _solutionEvents.AfterClosing += OnSolutionClosed; + + Logging.Logging.LogInfo("Initialization done"); } void OnSolutionOpened() { + Logging.Logging.LogInfo("A solution was loaded, checking languages"); + try { DTE dte = (DTE)GetService(typeof(DTE)); @@ -148,25 +209,34 @@ namespace CoatiSoftware.CoatiPlugin if (language == CodeModelLanguageConstants.vsCMLanguageVC || language == CodeModelLanguageConstants.vsCMLanguageMC) { + Logging.Logging.LogInfo("C/C++ project was detected"); enable = true; } } if (enable) { + Logging.Logging.LogInfo("Enabling plugin UI"); _menuItemSetActiveToken.Enabled = true; _menuItemCreateProject.Enabled = true; _menuItemCreateCDB.Enabled = true; } + else + { + Logging.Logging.LogInfo("No C/C++ project was detected"); + } } catch(Exception e) { DisplayMessage("Error", e.Message); + Logging.Logging.LogError(e.Message); } } void OnSolutionClosed() { + Logging.Logging.LogInfo("Solution closed, disabling plugin UI"); + _menuItemSetActiveToken.Enabled = false; _menuItemCreateProject.Enabled = false; _menuItemCreateCDB.Enabled = false; @@ -174,6 +244,8 @@ namespace CoatiSoftware.CoatiPlugin private void InitNetwork() { + Logging.Logging.LogInfo("Initializing Network with Server Port " + ServerPort.ToString() + " and Client Port " + ClientPort.ToString()); + Utility.AsynchronousSocketListener._port = ServerPort; Utility.AsynchronousSocketListener server = new Utility.AsynchronousSocketListener(); Utility.AsynchronousSocketListener._onReadCallback = new Utility.AsynchronousSocketListener.OnReadCallback(OnNetworkReadCallback); @@ -185,6 +257,22 @@ namespace CoatiSoftware.CoatiPlugin Utility.AsynchronousClient._onErrorCallback = new Utility.AsynchronousSocketListener.OnReadCallback(OnNetworkErrorCallback); } + private void InitLogging() + { + DTE dte = (DTE)GetService(typeof(DTE)); + + Logging.FileLogger fileLogger = new Logging.FileLogger(); + Logging.VSOutputLogger vsLogger = new Logging.VSOutputLogger(dte); + + Logging.LogManager.GetInstance().Loggers.Add(fileLogger); + Logging.LogManager.GetInstance().Loggers.Add(vsLogger); + Logging.LogManager.GetInstance().LoggingEnabled = LoggingEnabled; + + Logging.Obfuscation.NameObfuscator.Enabled(LogObfuscationEnabled); + + Logging.Logging.LogInfo("Logging initialized"); + } + private void OnCreateProject(List projects, string configurationName, string platformName, string targetDir, string fileName, string cStandard) { Wizard.WindowCreateCDB createCDB = new Wizard.WindowCreateCDB(); @@ -195,19 +283,56 @@ namespace CoatiSoftware.CoatiPlugin createCDB.FileName = fileName; createCDB.CStandard = cStandard; - createCDB.CallbackOnFinishedCreatingCDB = WriteCDBToFile; + createCDB.CallbackOnFinishedCreatingCDB = HandleFinishedCDB; createCDB.StartWorking(); createCDB.ShowDialog(); } + private void HandleFinishedCDB(Wizard.WindowCreateCDB.CreationResult creationResult) + { + if(creationResult._cdb != null && creationResult._cdbDirectory.Length > 0 && creationResult._cdbName.Length > 0) + { + WriteCDBToFile(creationResult._cdb, creationResult._cdbDirectory, creationResult._cdbName); + + //string message = NetworkProtocolUtility.createCreateProjectMessage(creationResult._cdbDirectory + "\\" + creationResult._cdbName, creationResult._headerDirectories); + + //Utility.AsynchronousClient.Send(message); + } + else + { + Logging.Logging.LogError("Invalid data received"); + } + } + private void WriteCDBToFile(SolutionParser.CompilationDatabase cdb, string directory, string fileName) { - string content = cdb.SerializeJSON(); - File.WriteAllText(directory + "\\" + fileName + ".json", content); + try + { + string content = cdb.SerializeJSON(); + File.WriteAllText(directory + "\\" + fileName + ".json", content); + } + catch(Exception e) + { + string foo = "Error"; + string bar = "Failed to write CDB '" + fileName + "' to directory \"" + directory + "\"\n"; + bar += "See log for details."; + + Wizard.WindowMessage wm = new Wizard.WindowMessage(); + wm.Title = foo; + wm.Message = bar; + wm.RefreshWindow(); + wm.ShowDialog(); + + bar = "Failed to write CDB '" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(fileName) + "' to directory \"" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(directory) + "\"\n"; + Logging.Logging.LogError(bar); + Logging.Logging.LogError("Exception: " + e.Message); + + return; + } string title = "CDB finished"; - string message = "The CDB " + fileName + " was created at directory \"" + directory + "\"\n"; + string message = "The CDB '" + fileName + "' was created at directory \"" + directory + "\"\n"; message += "You can now use it in Coati."; Wizard.WindowMessage windowMessage = new Wizard.WindowMessage(); @@ -215,6 +340,9 @@ namespace CoatiSoftware.CoatiPlugin windowMessage.Message = message; windowMessage.RefreshWindow(); windowMessage.ShowDialog(); + + message = "The CDB '" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(fileName) + "' was created at directory \"" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(directory) + "\"\n"; + Logging.Logging.LogInfo(message); } private void MenuItemCallback(object sender, EventArgs e) @@ -301,16 +429,20 @@ namespace CoatiSoftware.CoatiPlugin private void OnNetworkErrorCallback(string message) { + Logging.Logging.LogError("Network Error: " + message.ToString()); DisplayMessage("Coati Network Error", message); } private void OnFileUtilityError(string message) { + Logging.Logging.LogError("File Error: " + message.ToString()); DisplayMessage("Coati File Error", message); } private void OnServerPortChanged() { + Logging.Logging.LogInfo("Changing Server Port to " + ServerPort.ToString()); + Utility.AsynchronousSocketListener._port = ServerPort; _serverThread.Abort(); @@ -321,9 +453,35 @@ namespace CoatiSoftware.CoatiPlugin private void OnClientPortChanged() { + Logging.Logging.LogInfo("Changing Client Port to " + ClientPort.ToString()); + Utility.AsynchronousClient._port = ClientPort; } + private void OnLoggingToggled() + { + Logging.LogManager.GetInstance().LoggingEnabled = LoggingEnabled; + + if(LoggingEnabled) + { + Logging.Logging.LogInfo("Logging enabled"); + } + } + + private void OnObfuscationToggled() + { + Logging.Obfuscation.NameObfuscator.Enabled(LogObfuscationEnabled); + + if(LogObfuscationEnabled) + { + Logging.Logging.LogInfo("Log Obfuscation enabled"); + } + else + { + Logging.Logging.LogInfo("Log Obfuscation disabled"); + } + } + private void DisplayMessage(string title, string message) { IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); @@ -345,6 +503,8 @@ namespace CoatiSoftware.CoatiPlugin private void CreateCompilationDatabase(DTE dte) { + Logging.Logging.LogInfo("Preparing CDB dialog"); + Wizard.ProjectSetupWindow window = new Wizard.ProjectSetupWindow(); Utility.SolutionUtility.SolutionStructure projectStructure = Utility.SolutionUtility.GetSolutionVCProjects(dte); diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/FileLogger.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/FileLogger.cs new file mode 100644 index 00000000..637bd899 --- /dev/null +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/FileLogger.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; + +namespace CoatiSoftware.CoatiPlugin.Logging +{ + class FileLogger : ILogger + { + private static string _directory = ""; + private static string _fileNamePrefix = "Log_CoatiPlugin_"; + private static string _fileNameSufix = ".txt"; + + private string _fileName = ""; + + private Queue _messageBacklog = new Queue(); // stores messages if the log file was in use at original logging time + + public FileLogger() + { + DateTime time = DateTime.Now; + + string dateString = ""; + dateString += time.Year.ToString() + "-" + time.Month.ToString() + "-" + time.Day.ToString() + "_"; + dateString += time.Hour.ToString() + "-" + time.Minute.ToString() + "-" + time.Second.ToString(); + + _fileName = _fileNamePrefix + dateString + _fileNameSufix; + + _directory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + _directory += "\\Coati Software\\Plugins\\VS\\"; + + if(System.IO.Directory.Exists(_directory) == false) + { + System.IO.Directory.CreateDirectory(_directory); + } + } + + public void LogMessage(LogMessage message) + { + System.IO.StreamWriter writer = null; + + try + { + writer = System.IO.File.AppendText(_directory + _fileName); + + // write backlog to file first + while(_messageBacklog.Count > 0) + { + string bm = _messageBacklog.Dequeue(); + writer.WriteLine(bm); + } + + writer.WriteLine(message.ToString()); + writer.Close(); + } + catch(Exception e) + { + // well...file is still in use + _messageBacklog.Enqueue(message.ToString()); + } + finally + { + if(writer != null) + { + writer.Close(); + } + } + } + } +} diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/ILogger.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/ILogger.cs new file mode 100644 index 00000000..27a0e435 --- /dev/null +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/ILogger.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoatiSoftware.CoatiPlugin.Logging +{ + public interface ILogger + { + void LogMessage(LogMessage message); + } +} diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/LogManager.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/LogManager.cs new file mode 100644 index 00000000..f9a7c7f3 --- /dev/null +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/LogManager.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoatiSoftware.CoatiPlugin.Logging +{ + public class LogManager + { + private static LogManager _instance = null; + + private List _loggers = new List(); + + private bool _loggingEnabled = false; + + public List Loggers + { + get { return _loggers; } + set { _loggers = value; } + } + + public bool LoggingEnabled + { + get { return _loggingEnabled; } + set { _loggingEnabled = value; } + } + + private LogManager() + { + + } + + public static LogManager GetInstance() + { + if(_instance == null) + { + _instance = new LogManager(); + } + + return _instance; + } + + public void LogInfo(string message, string sourceFile, string callingFunction, int lineNumber) + { + LogMessage logMessage = new LogMessage(); + logMessage.Message = message; + logMessage.MessageType = LogMessage.LogMessageType.INFO; + logMessage.Time = DateTime.Now; + logMessage.SourceFile = sourceFile; + logMessage.CallingFunction = callingFunction; + logMessage.LineNumber = lineNumber; + + Log(logMessage); + } + + public void LogWarning(string message, string sourceFile, string callingFunction, int lineNumber) + { + LogMessage logMessage = new LogMessage(); + logMessage.Message = message; + logMessage.MessageType = LogMessage.LogMessageType.WARNING; + logMessage.Time = DateTime.Now; + logMessage.SourceFile = sourceFile; + logMessage.CallingFunction = callingFunction; + logMessage.LineNumber = lineNumber; + + Log(logMessage); + } + + public void LogError(string message, string sourceFile, string callingFunction, int lineNumber) + { + LogMessage logMessage = new LogMessage(); + logMessage.Message = message; + logMessage.MessageType = LogMessage.LogMessageType.ERROR; + logMessage.Time = DateTime.Now; + logMessage.SourceFile = sourceFile; + logMessage.CallingFunction = callingFunction; + logMessage.LineNumber = lineNumber; + + Log(logMessage); + } + + private void Log(LogMessage message) + { + if(_loggingEnabled == true) + { + foreach (ILogger logger in _loggers) + { + logger.LogMessage(message); + } + } + } + } +} diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/LogMessage.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/LogMessage.cs new file mode 100644 index 00000000..df8a437e --- /dev/null +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/LogMessage.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoatiSoftware.CoatiPlugin.Logging +{ + public class LogMessage + { + public enum LogMessageType + { + UNKNOWN = 0, + INFO, + WARNING, + ERROR + } + + private string _message = ""; + private DateTime _time = new DateTime(); + private LogMessageType _messageType = LogMessageType.UNKNOWN; + + private string _sourceFile = ""; + private string _callingFunction = ""; + private int _lineNumber = -1; + + public string Message + { + get { return _message; } + set { _message = value; } + } + + public DateTime Time + { + get { return _time; } + set { _time = value; } + } + + public LogMessageType MessageType + { + get { return _messageType; } + set { _messageType = value; } + } + + public string SourceFile + { + get { return _sourceFile; } + set { _sourceFile = value; } + } + + public string CallingFunction + { + get { return _callingFunction; } + set { _callingFunction = value; } + } + + public int LineNumber + { + get { return _lineNumber; } + set { _lineNumber = value; } + } + + public override string ToString() + { + string result = ""; + + result += _time.Hour.ToString() + ":" + _time.Minute.ToString() + ":"+ _time.Second.ToString(); + + result += "\t"; + + result += _messageType.ToString(); + + result += "\t"; + + result += _sourceFile + ":" + _lineNumber.ToString(); + + result += " ("; + + result += _callingFunction; + + result += ")"; + + result += "\t\t"; + + result += _message; + + return result; + } + } +} diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/Logging.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/Logging.cs new file mode 100644 index 00000000..35b100de --- /dev/null +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/Logging.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.CompilerServices; + +namespace CoatiSoftware.CoatiPlugin.Logging +{ + public class Logging + { + public static void LogInfo(string message, [CallerFilePath] string file = "", [CallerMemberName] string member = "", [CallerLineNumber] int line = 0) + { + int idx = file.LastIndexOf('\\'); + if(idx > -1) + { + file = file.Substring(idx + 1); + } + + LogManager.GetInstance().LogInfo(message, file, member, line); + } + + public static void LogWarning(string message, [CallerFilePath] string file = "", [CallerMemberName] string member = "", [CallerLineNumber] int line = 0) + { + int idx = file.LastIndexOf('\\'); + if (idx > -1) + { + file = file.Substring(idx + 1); + } + + LogManager.GetInstance().LogWarning(message, file, member, line); + } + + public static void LogError(string message, [CallerFilePath] string file = "", [CallerMemberName] string member = "", [CallerLineNumber] int line = 0) + { + int idx = file.LastIndexOf('\\'); + if (idx > -1) + { + file = file.Substring(idx + 1); + } + + LogManager.GetInstance().LogError(message, file, member, line); + } + } +} diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/Obfuscation/NameObfuscator.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/Obfuscation/NameObfuscator.cs new file mode 100644 index 00000000..baae9b60 --- /dev/null +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/Obfuscation/NameObfuscator.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; + +namespace CoatiSoftware.CoatiPlugin.Logging.Obfuscation +{ + class NameObfuscator + { + private static NameObfuscator _instance = null; + + private Dictionary _dictionary = new Dictionary(); + private bool _enabled = false; + + char _currentChar = 'a'; + int _currentInt = 0; + + private static string _directory = ""; + private static string _fileNamePrefix = "Dictionary_CoatiPlugin_"; + private static string _fileNameSufix = ".txt"; + + private string _fileName = ""; + + private Queue _messageBacklog = new Queue(); // stores messages if the log file was in use at original logging time + + private NameObfuscator() + { + DateTime time = DateTime.Now; + + string dateString = ""; + dateString += time.Year.ToString() + "-" + time.Month.ToString() + "-" + time.Day.ToString() + "_"; + dateString += time.Hour.ToString() + "-" + time.Minute.ToString() + "-" + time.Second.ToString(); + + _fileName = _fileNamePrefix + dateString + _fileNameSufix; + + _directory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + _directory += "\\Coati Software\\Plugins\\VS\\"; + + if (System.IO.Directory.Exists(_directory) == false) + { + System.IO.Directory.CreateDirectory(_directory); + } + } + + private static void CreateInstance() + { + if (_instance == null) + { + _instance = new NameObfuscator(); + } + } + + public static string GetObfuscatedName(string originalName) + { + CreateInstance(); + + if (_instance._enabled) + { + if (_instance._dictionary.ContainsKey(originalName)) + { + return _instance._dictionary[originalName]; + } + else + { + string newName = _instance.GetNewName(); + _instance._dictionary[originalName] = newName; + + _instance.WriteDictionaryEntryToFile(newName, originalName); // key and value reversed because that's the way the dictionary file is to be used... + return newName; + } + } + else + { + return originalName; + } + } + + public static void Enabled(bool enabled) + { + CreateInstance(); + + _instance._enabled = enabled; + } + + private string GetNewName() + { + string name = ""; + + name = _currentChar.ToString() + _currentInt.ToString(); + + ++_currentChar; + + if((int)_currentChar > 122) + { + _currentChar = 'a'; + ++_currentInt; + } + + return name; + } + + private void WriteDictionaryEntryToFile(string key, string value) + { + System.IO.StreamWriter writer = null; + string message = key + " - " + value; + + try + { + writer = System.IO.File.AppendText(_directory + _fileName); + + // write backlog to file first + while (_messageBacklog.Count > 0) + { + string bm = _messageBacklog.Dequeue(); + writer.WriteLine(bm); + } + + writer.WriteLine(message.ToString()); + writer.Close(); + } + catch (Exception e) + { + // well...file is still in use + _messageBacklog.Enqueue(message.ToString()); + } + finally + { + if (writer != null) + { + writer.Close(); + } + } + } + } +} diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/VSOutputLogger.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/VSOutputLogger.cs new file mode 100644 index 00000000..63ef21fb --- /dev/null +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Logging/VSOutputLogger.cs @@ -0,0 +1,80 @@ +using EnvDTE; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using System; +using System.Diagnostics; + +namespace CoatiSoftware.CoatiPlugin.Logging +{ + class VSOutputLogger : ILogger + { + private EnvDTE.DTE _dte = null; + private OutputWindowPane _pane = null; + + public VSOutputLogger(EnvDTE.DTE dte) + { + _dte = dte; + } + + public void LogMessage(LogMessage message) + { + if (message.MessageType == CoatiPlugin.Logging.LogMessage.LogMessageType.INFO) + { + Debug.WriteLine(message.Message, "Info"); + WriteToOutputWindow("Info: " + message.Message); + } + if (message.MessageType == CoatiPlugin.Logging.LogMessage.LogMessageType.WARNING) + { + Debug.WriteLine(message.Message, "Warning"); + WriteToOutputWindow("Warning: " + message.Message); + } + if (message.MessageType == CoatiPlugin.Logging.LogMessage.LogMessageType.ERROR) + { + Debug.WriteLine(message.Message, "Error"); + WriteToOutputWindow("Error: " + message.Message); + } + } + + private void WriteToOutputWindow(string message) + { + string paneName = "Coati Log"; + + if (_dte.Windows.Count > 0) + { + Window window = _dte.Windows.Item(EnvDTE.Constants.vsWindowKindOutput); + OutputWindow outputWindow = (OutputWindow)window.Object; + + OutputWindowPanes panes = outputWindow.OutputWindowPanes; + + if(_pane == null) + { + try + { + for (int i = 0; i < panes.Count; i++) + { + OutputWindowPane pane = panes.Item(i); + + if (pane.Name.Equals(paneName, StringComparison.CurrentCultureIgnoreCase)) + { + _pane = outputWindow.OutputWindowPanes.Item(i); + break; + } + } + } + catch (Exception e) + { + + } + } + + if (_pane == null) + { + _pane = outputWindow.OutputWindowPanes.Add(paneName); + } + + _pane.OutputString(message + '\n'); + } + } + } +} diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/NetworkProtocolUtility.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/NetworkProtocolUtility.cs index 4655cfbb..fd2a5a4b 100644 --- a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/NetworkProtocolUtility.cs +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/NetworkProtocolUtility.cs @@ -14,6 +14,7 @@ namespace CoatiSoftware.CoatiPlugin private static string s_endOfMessageToken = ""; private static string s_createProjectPrefix = "createProject"; + private static string s_createCDBProjectPrefix = "createCDBProject"; private static string s_ideId = "vs"; public class CursorPosition @@ -86,6 +87,28 @@ namespace CoatiSoftware.CoatiPlugin return message; } + public static string createCreateProjectMessage(string cdbPath, List headerPaths) + { + string message = s_createCDBProjectPrefix; + + message += s_divider; + + message += cdbPath; + + message += s_divider; + + foreach(string path in headerPaths) + { + message += path; + + message += s_divider; + } + + message += s_endOfMessageToken; + + return message; + } + public static CursorPosition parseSetCursorMessage(string message) { CursorPosition result = new CursorPosition(); diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/SolutionParser/SolutionParser.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/SolutionParser/SolutionParser.cs index 739c0525..c01e8721 100644 --- a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/SolutionParser/SolutionParser.cs +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/SolutionParser/SolutionParser.cs @@ -19,51 +19,22 @@ namespace CoatiSoftware.CoatiPlugin.SolutionParser static public List _additionalCompileFlags = new List(); - // Creates a cdb from all projects in the solution - // For a more selective approach use 'CreateCommandObjects(...)' for per-project compile commands and assamble the cdb yourself - static public CompilationDatabase CreateCompilationDatabase(DTE dte, string configurationName, string platformName) - { - if(dte == null) - { - return null; - } - - ReloadAll(dte); - - CompilationDatabase compilationDatabase = new CompilationDatabase(); - - EnvDTE.Solution solution = dte.Solution; - - EnvDTE.Projects projects = solution.Projects; - - foreach (EnvDTE.Project project in projects) - { - List cmdObjts = CreateCommandObjects(project, configurationName, platformName, "c11"); // TODO: retrieve real config/platform - - foreach(CommandObject cmdObj in cmdObjts) - { - compilationDatabase.AddCommandObject(cmdObj); - } - } - - UnloadReloadedProjects(dte); - - return compilationDatabase; - } + static public List _headerDirectories = new List(); public static List CreateCommandObjects(Project project, string configurationName, string platformName, string cStandard) { + Logging.Logging.LogInfo("Creating command objects from project " + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(project.Name)); + List result = new List(); DTE dte = project.DTE; - Guid projectGuid = Utility.SolutionUtility.ReloadProject(project); - - string version = dte.Version; + Guid projectGuid = Utility.ProjectUtility.ReloadProject(project); VCProject vcProject = project.Object as VCProject; if (vcProject == null) { + Logging.Logging.LogWarning("Project '" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(project.Name) + "' could not be converted to VCProject, skipping."); return result; } @@ -84,9 +55,11 @@ namespace CoatiSoftware.CoatiPlugin.SolutionParser VCConfiguration vcProjectConfig = GetProjectConfiguration(vcProject, configurationName, platformName); string cppStandard = GetCppStandardString(vcProjectConfig); + Logging.Logging.LogInfo("Found C++ standard " + cppStandard + "."); + foreach (EnvDTE.ProjectItem item in projectItems) { - CommandObject cmdObj = CreateCommandObject(item, includeDirectories, preprocessorDefinitions, cppStandard, cStandard); + CommandObject cmdObj = CreateCommandObject(item, includeDirectories, preprocessorDefinitions, cppStandard, cStandard, configurationName, platformName); if (cmdObj != null) { result.Add(cmdObj); @@ -95,15 +68,19 @@ namespace CoatiSoftware.CoatiPlugin.SolutionParser if(projectGuid != Guid.Empty) { - Utility.SolutionUtility.UnloadProject(projectGuid, dte); + Utility.ProjectUtility.UnloadProject(projectGuid, dte); } + _headerDirectories = _headerDirectories.Distinct().ToList(); + return result; } static private Tuple, List> GetProjectIncludeDirectoriesAndPreprocessorDefs(VCProject project, string configurationName, string platformName) { - List includeDirectories = new List(); + Logging.Logging.LogInfo("Attempting to retreive Include Directories and Preprocessor Definitions for project '" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(project.Name) + "'"); + + List includeDirectories = new List(); List preprocessorDefinitions = new List(); IEnumerable configurations = project.Configurations as IEnumerable; @@ -148,26 +125,42 @@ namespace CoatiSoftware.CoatiPlugin.SolutionParser } } - VCPlatform platform = vcProjectConfig.Platform as VCPlatform; - string platformIncludeDirectories = platform.IncludeDirectories; - string[] seperatedDirectories = platformIncludeDirectories.Split(';'); - - foreach (string directory in seperatedDirectories) + try { - string resolvedDirectory = ResolveVSMacro(vcProjectConfig, directory); - string[] splitResolvedDirectory = resolvedDirectory.Split(';'); // resolved macros might result in concatenated paths + VCPlatform platform = vcProjectConfig.Platform as VCPlatform; + string platformIncludeDirectories = platform.IncludeDirectories; + string[] seperatedDirectories = platformIncludeDirectories.Split(';'); - foreach (string p in splitResolvedDirectory) + foreach (string directory in seperatedDirectories) { - includeDirectories.Add(p); + string resolvedDirectory = ResolveVSMacro(vcProjectConfig, directory); + string[] splitResolvedDirectory = resolvedDirectory.Split(';'); // resolved macros might result in concatenated paths + + foreach (string p in splitResolvedDirectory) + { + includeDirectories.Add(p); + } } } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); + return new Tuple, List>(new List(), new List()); + } + } + else + { + Logging.Logging.LogWarning("Could not retreive Project Configuration. No include directories or preprocessor definitions could be retreived."); + return new Tuple, List>(new List(), new List()); } includeDirectories = includeDirectories.Distinct().ToList(); preprocessorDefinitions = preprocessorDefinitions.Distinct().ToList(); + Logging.Logging.LogInfo("Found " + includeDirectories.Count.ToString() + " distinct include directories and " + preprocessorDefinitions.Count.ToString() + " distinct preprocessor definitions."); + Logging.Logging.LogInfo("Attempting to resolve and clean up."); + for (int i = 0; i < includeDirectories.Count; i++) { string path = includeDirectories.ElementAt(i).Replace("\\", "/"); // backslashes would cause some string-escaping hassles... @@ -204,6 +197,8 @@ namespace CoatiSoftware.CoatiPlugin.SolutionParser } } + Logging.Logging.LogInfo("Found " + includeDirectories.Count.ToString() + " include directories and " + preprocessorDefinitions.Count.ToString() + " preprocessor definitions"); + return new Tuple, List>(includeDirectories, preprocessorDefinitions); } @@ -227,101 +222,146 @@ namespace CoatiSoftware.CoatiPlugin.SolutionParser } } + Logging.Logging.LogError("Failed to find project config matching with \"" + configurationName + "\""); + return null; } - static private CommandObject CreateCommandObject(EnvDTE.ProjectItem item, List includeDirectories, List preprocessorDefinitions, string vcStandard, string cStandard) + static private CommandObject CreateCommandObject(EnvDTE.ProjectItem item, List includeDirectories, List preprocessorDefinitions, string vcStandard, string cStandard, string configurationName, string platformName) { - DTE dte = item.DTE; + Logging.Logging.LogInfo("Starting to create Command Object from item '" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(item.Name) + "'"); - if (item.FileCodeModel != null && item.FileCodeModel.Language == CodeModelLanguageConstants.vsCMLanguageVC) + try { - CommandObject commandObject = new CommandObject(); - commandObject.File = item.Name; + DTE dte = item.DTE; - // only write source files to cdb, headers are implicit - if (CheckIsHeader(item)) + if (dte == null) { - return null; + Logging.Logging.LogError("Failed to retreive DTE object. Abort creating command object."); } - VCFile vcFile = item.Object as VCFile; - string subType = vcFile.SubType; - - VCProject project = vcFile.project; - VCConfiguration vcConfig = GetProjectConfiguration(project, "Release", "Win32"); - - VCFileConfiguration fc = vcFile.GetFileConfigurationForProjectConfiguration(vcConfig); - VCCLCompilerTool t = fc.Tool as VCCLCompilerTool; - - string additionalOptions = t.AdditionalOptions; - CompileAsOptions compileAs = t.CompileAs; // VCCLCompilerToolShim - - if(additionalOptions == "$(NOINHERIT)") + if (item.FileCodeModel != null && item.FileCodeModel.Language == CodeModelLanguageConstants.vsCMLanguageVC) { - additionalOptions = ""; - } + CommandObject commandObject = new CommandObject(); + commandObject.File = item.Name; - // check wheter it's a .c file, we don't want that... - // TODO: there is a property for comilation as .c or .cpp file (/TC and /TP), try to retrieve it - string extension = item.Properties.Item("Extension").Value.ToString(); - if (compileAs == CompileAsOptions.compileAsC) + // only write source files to cdb, headers are implicit + if (CheckIsHeader(item)) + { + Properties props = item.Properties; + foreach (Property prop in props) + { + string propName = prop.Name; + string propValue = prop.Value as String; + + if (propName == "FullPath") + { + int i = propValue.LastIndexOf('\\'); + + propValue = propValue.Substring(0, i); + + _headerDirectories.Add(propValue); + } + } + + return null; + } + + VCFile vcFile = item.Object as VCFile; + string subType = vcFile.SubType; + + VCProject project = vcFile.project; + VCConfiguration vcConfig = GetProjectConfiguration(project, configurationName, platformName); + + if(vcConfig == null) + { + Logging.Logging.LogError("Project Configuration is null."); + + return null; + } + + VCFileConfiguration fc = vcFile.GetFileConfigurationForProjectConfiguration(vcConfig); + VCCLCompilerTool t = fc.Tool as VCCLCompilerTool; + + string additionalOptions = t.AdditionalOptions; + CompileAsOptions compileAs = t.CompileAs; // VCCLCompilerToolShim + + if (additionalOptions == "$(NOINHERIT)") + { + additionalOptions = ""; + } + + // check wheter it's a .c file, we don't want that... + // TODO: there is a property for comilation as .c or .cpp file (/TC and /TP), try to retrieve it + string extension = item.Properties.Item("Extension").Value.ToString(); + if (compileAs == CompileAsOptions.compileAsC) + { + vcStandard = "-std=" + cStandard; + } + + // if a language standard was defined in the additional options the 'vcStandard' string is not used + if (additionalOptions.Contains("-std=")) + { + vcStandard = ""; + } + + string directory = item.Properties.Item("FullPath").Value.ToString(); + + int idx = directory.LastIndexOf('\\'); + if (idx != -1) + { + directory = directory.Substring(0, idx + 1); + } + + directory = directory.Replace('\\', '/'); + commandObject.File = directory + item.Name; + + commandObject.Directory = System.IO.Path.GetDirectoryName(dte.Solution.FullName); // TODO: replace with actual cdb location + commandObject.Directory = commandObject.Directory.Replace('\\', '/'); + + commandObject.Command = "clang-tool "; + + foreach (string flag in _compatibilityFlags) + { + commandObject.Command += flag + " "; + } + + commandObject.Command += _compatibilityVersionFlag + " "; + + foreach (string dir in includeDirectories) + { + commandObject.Command += " -isystem '" + dir + "' "; // using '-isystem' because it allows for use of quotes and pointy brackets in source files. In other words it's more robust. It's slower than '-I' though + } + + foreach (string prepDef in preprocessorDefinitions) + { + commandObject.Command += " -D " + prepDef + " "; + } + + foreach (string flag in _additionalCompileFlags) + { + commandObject.Command += " -D " + flag + " "; + } + + commandObject.Command += vcStandard + " "; + + commandObject.Command += additionalOptions + " "; + + commandObject.Command += "'" + commandObject.File + "'"; + + return commandObject; + } + else { - vcStandard = "-std=" + cStandard; + Logging.Logging.LogInfo("Item discarded, wrong code model"); } - - // if a language standard was defined in the additional options the 'vcStandard' string is not used - if(additionalOptions.Contains("-std=")) - { - vcStandard = ""; - } - - string directory = item.Properties.Item("FullPath").Value.ToString(); - - int idx = directory.LastIndexOf('\\'); - if (idx != -1) - { - directory = directory.Substring(0, idx + 1); - } - - directory = directory.Replace('\\', '/'); - commandObject.File = directory + item.Name; - - commandObject.Directory = System.IO.Path.GetDirectoryName(dte.Solution.FullName); // TODO: replace with actual cdb location - commandObject.Directory = commandObject.Directory.Replace('\\', '/'); - - commandObject.Command = "clang-tool "; - - foreach (string flag in _compatibilityFlags) - { - commandObject.Command += flag + " "; - } - - commandObject.Command += _compatibilityVersionFlag + " "; - - foreach (string dir in includeDirectories) - { - commandObject.Command += " -isystem '" + dir + "' "; // using '-isystem' because it allows for use of quotes and pointy brackets in source files. In other words it's more robust. It's slower than '-I' though - } - - foreach (string prepDef in preprocessorDefinitions) - { - commandObject.Command += " -D " + prepDef + " "; - } - - foreach(string flag in _additionalCompileFlags) - { - commandObject.Command += " -D " + flag + " "; - } - - commandObject.Command += vcStandard + " "; - - commandObject.Command += additionalOptions + " "; - - commandObject.Command += "'" + commandObject.File + "'"; - - return commandObject; } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); + } + + Logging.Logging.LogError("Failed to create command object."); return null; } @@ -332,21 +372,28 @@ namespace CoatiSoftware.CoatiPlugin.SolutionParser string propString = ""; - foreach (Property prop in props) + try { - string propName = prop.Name; - string propValue = prop.Value as String; - - propString += propName + " - " + propValue + "; "; - - if (propName == "ItemType") + foreach (Property prop in props) { - if (propValue as String == "ClInclude") + string propName = prop.Name; + string propValue = prop.Value as String; + + propString += propName + " - " + propValue + "; "; + + if (propName == "ItemType") { - return true; + if (propValue as String == "ClInclude") + { + return true; + } } } } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); + } return false; } @@ -355,15 +402,22 @@ namespace CoatiSoftware.CoatiPlugin.SolutionParser { string result = path; - Tuple potentialMacroPosition = Utility.StringUtility.FindFirstRange(path, "$(", ")"); - - if (potentialMacroPosition != null) + try { - string potentialMacro = path.Substring(potentialMacroPosition.Item1, potentialMacroPosition.Item2 - potentialMacroPosition.Item1 + 1); + Tuple potentialMacroPosition = Utility.StringUtility.FindFirstRange(path, "$(", ")"); - string resolvedMacro = vcProjectConfig.Evaluate(potentialMacro); + if (potentialMacroPosition != null) + { + string potentialMacro = path.Substring(potentialMacroPosition.Item1, potentialMacroPosition.Item2 - potentialMacroPosition.Item1 + 1); - result = path.Substring(0, potentialMacroPosition.Item1) + resolvedMacro + path.Substring(potentialMacroPosition.Item2 + 1); + string resolvedMacro = vcProjectConfig.Evaluate(potentialMacro); + + result = path.Substring(0, potentialMacroPosition.Item1) + resolvedMacro + path.Substring(potentialMacroPosition.Item2 + 1); + } + } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); } return result; @@ -378,11 +432,11 @@ namespace CoatiSoftware.CoatiPlugin.SolutionParser EnvDTE.Solution solution = dte.Solution; - EnvDTE.Projects projects = solution.Projects; + List projects = Utility.SolutionUtility.GetSolutionProjectList(dte); foreach (EnvDTE.Project project in projects) { - _reloadedProjectGuids.Add(Utility.SolutionUtility.ReloadProject(project)); + _reloadedProjectGuids.Add(Utility.ProjectUtility.ReloadProject(project)); } } @@ -390,12 +444,14 @@ namespace CoatiSoftware.CoatiPlugin.SolutionParser { foreach(Guid guid in _reloadedProjectGuids) { - Utility.SolutionUtility.UnloadProject(guid, dte); + Utility.ProjectUtility.UnloadProject(guid, dte); } } static private void SetCompatibilityVersionFlag(VCProject project, string configurationName, string platformName) { + Logging.Logging.LogInfo("Determining CL.exe (C++ compiler) version"); + VCConfiguration vcProjectConfig = GetProjectConfiguration(project, configurationName, platformName); if (vcProjectConfig != null) @@ -403,60 +459,88 @@ namespace CoatiSoftware.CoatiPlugin.SolutionParser IEnumerable projectTools = vcProjectConfig.Tools as IEnumerable; foreach (Object tool in projectTools) { - VCCLCompilerTool compilerTool = tool as VCCLCompilerTool; - - if (compilerTool != null) + try { - int majorCompilerVersion = GetCLMajorVersion(compilerTool, vcProjectConfig); + VCCLCompilerTool compilerTool = tool as VCCLCompilerTool; - if (majorCompilerVersion > -1) + if (compilerTool != null) { - _compatibilityVersionFlag = _compatibilityVersionFlagBase + majorCompilerVersion.ToString(); - return; + int majorCompilerVersion = GetCLMajorVersion(compilerTool, vcProjectConfig); + + if (majorCompilerVersion > -1) + { + Logging.Logging.LogInfo("Found compiler version " + majorCompilerVersion.ToString()); + + _compatibilityVersionFlag = _compatibilityVersionFlagBase + majorCompilerVersion.ToString(); + return; + } } } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); + } } } + else + { + Logging.Logging.LogWarning("Failed to retreive VC Project Configuration. Using default compiler version"); + } } static private int GetCLMajorVersion(VCCLCompilerTool compilerTool, VCConfiguration vcProjectConfig) { - if(compilerTool == null || vcProjectConfig == null) + Logging.Logging.LogInfo("Looking up CL.exe (C++ compiler)"); + + if (compilerTool == null || vcProjectConfig == null) { return -1; } - VCPlatform platform = vcProjectConfig.Platform as VCPlatform; - string executableDirectories = platform.ExecutableDirectories; - string[] seperatedDirectories = executableDirectories.Split(';'); - - List finalDirectories = new List(); - foreach (string directory in seperatedDirectories) + try { - string resolvedDirectory = ResolveVSMacro(vcProjectConfig, directory); - string[] splitResolvedDirectory = resolvedDirectory.Split(';'); // resolved macros might result in concatenated paths + VCPlatform platform = vcProjectConfig.Platform as VCPlatform; + string executableDirectories = platform.ExecutableDirectories; + string[] seperatedDirectories = executableDirectories.Split(';'); - foreach(string d in splitResolvedDirectory) + List finalDirectories = new List(); + foreach (string directory in seperatedDirectories) { - finalDirectories.Add(d); + string resolvedDirectory = ResolveVSMacro(vcProjectConfig, directory); + string[] splitResolvedDirectory = resolvedDirectory.Split(';'); // resolved macros might result in concatenated paths + + foreach (string d in splitResolvedDirectory) + { + finalDirectories.Add(d); + } + } + + string toolPath = compilerTool.ToolPath; + + Logging.Logging.LogInfo("Found " + finalDirectories.Count.ToString() + " possible compiler directories."); + + foreach (string fd in finalDirectories) + { + string path = fd + "\\" + toolPath; + + if (File.Exists(path)) + { + FileVersionInfo info = FileVersionInfo.GetVersionInfo(path); + int version = info.FileMajorPart; + + Logging.Logging.LogInfo("Found compiler location. Compiler tool version is " + version.ToString()); + + return version; + } } } - - string toolPath = compilerTool.ToolPath; - - foreach(string fd in finalDirectories) + catch(Exception e) { - string path = fd + "\\" + toolPath; - - if(File.Exists(path)) - { - FileVersionInfo info = FileVersionInfo.GetVersionInfo(path); - int version = info.FileMajorPart; - - return version; - } + Logging.Logging.LogError("Exception: " + e.Message); } + Logging.Logging.LogWarning("Failed to find C++ compiler tool."); + return -1; } @@ -472,27 +556,44 @@ namespace CoatiSoftware.CoatiPlugin.SolutionParser string result = ""; - IVCRulePropertyStorage rules = vcProjectConfig.Rules.Item("ConfigurationGeneral"); + IVCRulePropertyStorage rules = null; + + try + { + rules = vcProjectConfig.Rules.Item("ConfigurationGeneral"); + } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); + return ""; + } if (rules != null) { - string toolset = rules.GetUnevaluatedPropertyValue("PlatformToolset"); - - string justNumbers = new String(toolset.Where(Char.IsDigit).ToArray()); - - int versionNumber = int.Parse(justNumbers); - - if (versionNumber < 120) // version 11 (2012) + try { - result = "-std=c++11"; + string toolset = rules.GetUnevaluatedPropertyValue("PlatformToolset"); + + string justNumbers = new String(toolset.Where(Char.IsDigit).ToArray()); + + int versionNumber = int.Parse(justNumbers); + + if (versionNumber < 120) // version 11 (2012) + { + result = "-std=c++11"; + } + else if (versionNumber < 130) // version 12 (2013) + { + result = "-std=c++14"; + } + else if (versionNumber < 150) // version 14 (2015) + { + result = "-std=c++14"; + } } - else if (versionNumber < 130) // version 12 (2013) + catch(Exception e) { - result = "-std=c++14"; - } - else if (versionNumber < 150) // version 14 (2015) - { - result = "-std=c++14"; + Logging.Logging.LogError("Exception: " + e.Message); } } diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/FileUtility.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/FileUtility.cs index 09896265..49280be5 100644 --- a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/FileUtility.cs +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/FileUtility.cs @@ -21,10 +21,16 @@ namespace CoatiSoftware.CoatiPlugin.Utility } catch (Exception e) { + Logging.Logging.LogError("Exception: " + e.Message); + if (_errorCallback != null) { _errorCallback("Failed to open file at " + fileName); } + + string message = "Failed to open file at " + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(fileName); + Logging.Logging.LogError(message); + return false; } } @@ -37,9 +43,14 @@ namespace CoatiSoftware.CoatiPlugin.Utility } catch (Exception e) { + Logging.Logging.LogError("Exception: " + e.Message); + if (_errorCallback != null) { _errorCallback("Failed to set cursor to position [" + lineNumber.ToString() + "," + columnNumber.ToString() + "]"); + + string message = "Failed to set cursor to position [" + lineNumber.ToString() + "," + columnNumber.ToString() + "]"; + Logging.Logging.LogError(message); } } } diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/NetworkUtility.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/NetworkUtility.cs index 46a77bde..e384ee49 100644 --- a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/NetworkUtility.cs +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/NetworkUtility.cs @@ -63,57 +63,75 @@ namespace CoatiSoftware.CoatiPlugin.Utility } catch (Exception e) { - //if(_onErrorCallback != null) - //{ - // _onErrorCallback(e.ToString()); - //} + Logging.Logging.LogError("Exception: " + e.Message); } } public static void AcceptCallback(IAsyncResult ar) { - _allDone.Set(); + try + { + _allDone.Set(); - Socket listener = (Socket)ar.AsyncState; - Socket handler = listener.EndAccept(ar); + Socket listener = (Socket)ar.AsyncState; + Socket handler = listener.EndAccept(ar); - StateObject state = new StateObject(); - state._workSocket = handler; - handler.BeginReceive(state._buffer, 0, StateObject._bufferSize, 0, new AsyncCallback(ReadCallback), state); + StateObject state = new StateObject(); + state._workSocket = handler; + handler.BeginReceive(state._buffer, 0, StateObject._bufferSize, 0, new AsyncCallback(ReadCallback), state); + } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); + } } public static void ReadCallback(IAsyncResult ar) { - String content = String.Empty; - - StateObject state = (StateObject)ar.AsyncState; - Socket handler = state._workSocket; - - int bytesRead = handler.EndReceive(ar); - - if (bytesRead > 0) + try { - state._stringBuilder.Append(Encoding.ASCII.GetString(state._buffer, 0, bytesRead)); + string content = String.Empty; - content = state._stringBuilder.ToString(); - if (content.IndexOf(_endOfMessageToken) > -1) + StateObject state = (StateObject)ar.AsyncState; + Socket handler = state._workSocket; + + int bytesRead = handler.EndReceive(ar); + + if (bytesRead > 0) { - if (_onReadCallback != null) + state._stringBuilder.Append(Encoding.ASCII.GetString(state._buffer, 0, bytesRead)); + + content = state._stringBuilder.ToString(); + if (content.IndexOf(_endOfMessageToken) > -1) { - _onReadCallback(content); + if (_onReadCallback != null) + { + _onReadCallback(content); + } + } + else + { + handler.BeginReceive(state._buffer, 0, StateObject._bufferSize, 0, new AsyncCallback(ReadCallback), state); } } - else - { - handler.BeginReceive(state._buffer, 0, StateObject._bufferSize, 0, new AsyncCallback(ReadCallback), state); - } + } + catch(Exception e) + { + Logging.Logging.LogError("Excpetion: " + e.Message); } } private static void Send(Socket handler, String data) { - byte[] byteData = Encoding.ASCII.GetBytes(data); - handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler); + try + { + byte[] byteData = Encoding.ASCII.GetBytes(data); + handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler); + } + catch(Exception e) + { + Logging.Logging.LogError("Excpetion: " + e.Message); + } } private static void SendCallback(IAsyncResult ar) @@ -123,10 +141,12 @@ namespace CoatiSoftware.CoatiPlugin.Utility Socket handler = (Socket)ar.AsyncState; int bytesSent = handler.EndSend(ar); - Console.WriteLine("Sent {0} bytes to client.", bytesSent); + Logging.Logging.LogInfo("Sent " + bytesSent.ToString() + " bytes to client."); } catch (Exception e) { + Logging.Logging.LogError("Excpetion: " + e.Message); + if (_onErrorCallback != null) { _onErrorCallback(e.ToString()); @@ -159,6 +179,9 @@ namespace CoatiSoftware.CoatiPlugin.Utility client.EndConnect(ar); client.Shutdown(SocketShutdown.Both); client.Close(); + + Logging.Logging.LogWarning("Connection timed out, message was not sent"); + return; } @@ -186,6 +209,8 @@ namespace CoatiSoftware.CoatiPlugin.Utility } catch (Exception e) { + Logging.Logging.LogError("Excpetion: " + e.Message); + if (_onErrorCallback != null) { _onErrorCallback(e.ToString()); @@ -195,9 +220,16 @@ namespace CoatiSoftware.CoatiPlugin.Utility private static void Send(Socket client, String data) { - byte[] byteData = Encoding.ASCII.GetBytes(data); + try + { + byte[] byteData = Encoding.ASCII.GetBytes(data); - client.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), client); + client.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), client); + } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); + } } private static void SendCallback(IAsyncResult ar) @@ -212,6 +244,8 @@ namespace CoatiSoftware.CoatiPlugin.Utility } catch (Exception e) { + Logging.Logging.LogError("Excpetion: " + e.Message); + if (_onErrorCallback != null) { if (e is ObjectDisposedException) @@ -221,6 +255,7 @@ namespace CoatiSoftware.CoatiPlugin.Utility } else { + _onErrorCallback(e.ToString()); } } diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/ProjectUtility.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/ProjectUtility.cs index ef01b48a..ff99f7dd 100644 --- a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/ProjectUtility.cs +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/ProjectUtility.cs @@ -1,4 +1,8 @@ using EnvDTE; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using System; using System.Collections; using System.Collections.Generic; @@ -10,17 +14,24 @@ namespace CoatiSoftware.CoatiPlugin.Utility { List projectItems = GetProjectItems(project); - foreach (EnvDTE.ProjectItem item in projectItems) + try { - if (item.FileCodeModel != null && item.FileCodeModel.Language == CodeModelLanguageConstants.vsCMLanguageVC) + foreach (EnvDTE.ProjectItem item in projectItems) { - string extension = item.Properties.Item("Extension").Value.ToString(); - if (extension == ".c") + if (item.FileCodeModel != null && item.FileCodeModel.Language == CodeModelLanguageConstants.vsCMLanguageVC) { - return true; + string extension = item.Properties.Item("Extension").Value.ToString(); + if (extension == ".c") + { + return true; + } } } } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); + } return false; } @@ -34,13 +45,13 @@ namespace CoatiSoftware.CoatiPlugin.Utility while (itemEnumerator.MoveNext()) { ProjectItem currentItem = (ProjectItem)itemEnumerator.Current; - items.Add(GetProjectItemsRecursive(currentItem, ref items)); + items.Add(GetProjectSubItemsRecursive(currentItem, ref items)); } return items; } - static private ProjectItem GetProjectItemsRecursive(ProjectItem item, ref List projectItems) + static private ProjectItem GetProjectSubItemsRecursive(ProjectItem item, ref List projectItems) { if (item.ProjectItems == null) { @@ -52,10 +63,104 @@ namespace CoatiSoftware.CoatiPlugin.Utility while (items.MoveNext()) { ProjectItem currentItem = (ProjectItem)items.Current; - projectItems.Add(GetProjectItemsRecursive(currentItem, ref projectItems)); + projectItems.Add(GetProjectSubItemsRecursive(currentItem, ref projectItems)); } return item; } + + // returns true if the project was reloaded, false if the project did not need to be reloaded + static public Guid ReloadProject(Project project) + { + Logging.Logging.LogInfo("Attempting to reload project"); + + try + { + if (project != null && project.Kind == EnvDTE.Constants.vsProjectKindUnmodeled) + { + DTE dte = project.DTE; + + ServiceProvider sp = new ServiceProvider(dte as Microsoft.VisualStudio.OLE.Interop.IServiceProvider); + IVsSolution vsSolution = sp.GetService(typeof(SVsSolution)) as IVsSolution; + + IVsHierarchy hierarchy; + + string solutionDirectory = ""; + string solutionFile = ""; + string userOptions = ""; + vsSolution.GetSolutionInfo(out solutionDirectory, out solutionFile, out userOptions); + + vsSolution.GetProjectOfUniqueName(solutionDirectory + project.UniqueName, out hierarchy); + + if (hierarchy != null) + { + Guid projectGuid; + + hierarchy.GetGuidProperty( + VSConstants.VSITEMID_ROOT, + (int)__VSHPROPID.VSHPROPID_ProjectIDGuid, + out projectGuid); + + if (projectGuid != null) + { + Logging.Logging.LogInfo("Project '" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(project.Name) + "' with GUID {" + projectGuid.ToString() + "} loaded."); + (vsSolution as IVsSolution4).ReloadProject(projectGuid); + return projectGuid; + } + else + { + Logging.Logging.LogError("Failed to load project '" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(project.Name) + "'"); + } + } + else + { + Logging.Logging.LogError("Failed to retreive IVsHierarchy. Can't load project '" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(project.Name) + "'"); + } + } + else + { + if (project == null) + { + Logging.Logging.LogWarning("Project is null"); + } + else + { + Logging.Logging.LogInfo("Project '" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(project.Name) + "' is already loaded"); + } + } + } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); + + return Guid.Empty; + } + + return Guid.Empty; + } + + static public void UnloadProject(Guid guid, DTE dte) + { + Logging.Logging.LogInfo("Attempting to unload project with GUID {" + guid.ToString() + "}"); + + if (dte == null) + { + return; + } + + try + { + ServiceProvider sp = new ServiceProvider(dte as Microsoft.VisualStudio.OLE.Interop.IServiceProvider); + IVsSolution vsSolution = sp.GetService(typeof(SVsSolution)) as IVsSolution; + + (vsSolution as IVsSolution4).UnloadProject(guid, (uint)_VSProjectUnloadStatus.UNLOADSTATUS_UnloadedByUser); + + Logging.Logging.LogInfo("Done unloading project with GUID {" + guid.ToString() + "}"); + } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); + } + } } } diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/SolutionUtility.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/SolutionUtility.cs index b5db62c1..b6553f4c 100644 --- a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/SolutionUtility.cs +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/SolutionUtility.cs @@ -55,9 +55,18 @@ namespace CoatiSoftware.CoatiPlugin.Utility public static String GetSolutionPath(DTE dte) { - EnvDTE.Solution solution = dte.Solution; + try + { + EnvDTE.Solution solution = dte.Solution; - return solution.FullName; + return solution.FullName; + } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); + + return "N/A"; + } } public static SolutionStructure GetSolutionVCProjects(DTE dte) @@ -76,34 +85,48 @@ namespace CoatiSoftware.CoatiPlugin.Utility foreach(Project project in solutionProjects) { - if(project.Kind == EnvDTE.Constants.vsProjectKindUnmodeled) // not loaded + try { - continue; - } - - // check it's a c/c++ project - if(project.CodeModel != null) - { - if(project.CodeModel.Language != CodeModelLanguageConstants.vsCMLanguageVC) + if (project.Kind == EnvDTE.Constants.vsProjectKindUnmodeled) // not loaded { continue; } + + // check it's a c/c++ project + if (project.CodeModel != null) + { + if (project.CodeModel.Language != CodeModelLanguageConstants.vsCMLanguageVC) + { + continue; + } + } + + if (project.Kind == "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") + { + SolutionStructure.ProjectNode projectNode = new SolutionStructure.ProjectNode(); + projectNode.Name = project.Name; + projectNode.Project = project; + projectNode.Include = false; + + projectStructure.Nodes.Add(projectNode); + } + else + { + SolutionStructure.Node folderNode = GetSubProjects(project); + + if (folderNode != null) + { + projectStructure.Nodes.Add(folderNode); + } + else + { + Logging.Logging.LogWarning("Subnode was NULL"); + } + } } - - if(project.Kind == "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") + catch(Exception e) { - SolutionStructure.ProjectNode projectNode = new SolutionStructure.ProjectNode(); - projectNode.Name = project.Name; - projectNode.Project = project; - projectNode.Include = false; - - projectStructure.Nodes.Add(projectNode); - } - else - { - SolutionStructure.Node folderNode = GetSubProjects(project); - - projectStructure.Nodes.Add(folderNode); + Logging.Logging.LogError("Exception: " + e.Message); } } @@ -115,41 +138,58 @@ namespace CoatiSoftware.CoatiPlugin.Utility ProjectItems projectItems = project.ProjectItems; List items = new List(); - foreach (ProjectItem item in projectItems) + try { - Project p = item.Object as Project; - if(p != null) + foreach (ProjectItem item in projectItems) { - items.Add(p); - } - } - - if(items.Count > 0) - { - SolutionStructure.FolderNode folderNode = new SolutionStructure.FolderNode(); - folderNode.Name = project.Name; - - for (int i = 0; i < items.Count; i++) - { - Project item = items[i]; - - SolutionStructure.Node subFolderNode = GetSubProjects(item); - folderNode.SubNodes.Add(subFolderNode); + Project p = item.Object as Project; + if (p != null) + { + items.Add(p); + } } - return folderNode; - } - else - { - SolutionStructure.ProjectNode projectNode = new SolutionStructure.ProjectNode(); - projectNode.Name = project.Name; - projectNode.Project = project; - projectNode.Include = false; + if (items.Count > 0) + { + SolutionStructure.FolderNode folderNode = new SolutionStructure.FolderNode(); + folderNode.Name = project.Name; - return projectNode; + for (int i = 0; i < items.Count; i++) + { + Project item = items[i]; + + SolutionStructure.Node subFolderNode = GetSubProjects(item); + + if(subFolderNode != null) + { + folderNode.SubNodes.Add(subFolderNode); + } + else + { + Logging.Logging.LogWarning("SubNode was NULL"); + } + } + + return folderNode; + } + else + { + SolutionStructure.ProjectNode projectNode = new SolutionStructure.ProjectNode(); + projectNode.Name = project.Name; + projectNode.Project = project; + projectNode.Include = false; + + return projectNode; + } + } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); + return null; } } + // Deprecated: remove when old project parsing is retired public static List GetSolutionProjectsFullNames(DTE dte) { List projectNames = new List(); @@ -161,7 +201,7 @@ namespace CoatiSoftware.CoatiPlugin.Utility foreach (EnvDTE.Project project in projects) { - guids.Add(ReloadProject(project)); + guids.Add(ProjectUtility.ReloadProject(project)); } projects = solution.Projects; @@ -173,45 +213,17 @@ namespace CoatiSoftware.CoatiPlugin.Utility foreach (Guid guid in guids) { - UnloadProject(guid, dte); + ProjectUtility.UnloadProject(guid, dte); } return projectNames; } - public static List> GetSolutionProjectItems(DTE dte) - { - List> projectItems = new List>(); - - EnvDTE.Solution solution = dte.Solution; - EnvDTE.Projects projects = solution.Projects; - - foreach (EnvDTE.Project project in projects) - { - EnvDTE.ProjectItems items = project.ProjectItems; - - List pItems = new List(); - - foreach(EnvDTE.ProjectItem item in items) - { - for (short i = 0; i < item.FileCount; i++) - { - pItems.Add(item.get_FileNames(i)); - } - } - - projectItems.Add(pItems); - } - - return projectItems; - } - public static List GetSolutionLanguages(DTE dte) { List languages = new List(); - EnvDTE.Solution solution = dte.Solution; - EnvDTE.Projects projects = solution.Projects; + List projects = GetSolutionProjectList(dte); foreach (EnvDTE.Project project in projects) { @@ -229,9 +241,18 @@ namespace CoatiSoftware.CoatiPlugin.Utility public static bool GetSolutionIsSaved(DTE dte) { - EnvDTE.Solution solution = dte.Solution; + try + { + EnvDTE.Solution solution = dte.Solution; - return solution.Saved; + return solution.Saved; + } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); + + throw e; + } } public static List> GetConfigurationAndPlatformNames(DTE dte) @@ -259,125 +280,84 @@ namespace CoatiSoftware.CoatiPlugin.Utility return result; } - foreach (SolutionConfiguration2 solutionConfiguration in solutionBuild.SolutionConfigurations) + try { - foreach (SolutionContext context in solutionConfiguration.SolutionContexts) + foreach (SolutionConfiguration2 solutionConfiguration in solutionBuild.SolutionConfigurations) { - string configurationName = context.ConfigurationName; - configNames.Add(configurationName); + foreach (SolutionContext context in solutionConfiguration.SolutionContexts) + { + string configurationName = context.ConfigurationName; + configNames.Add(configurationName); - string platformName = context.PlatformName; - platformNames.Add(platformName); + string platformName = context.PlatformName; + platformNames.Add(platformName); + } } + + configNames = configNames.Distinct().ToList(); + platformNames = platformNames.Distinct().ToList(); + + result.Add(configNames); + result.Add(platformNames); + } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); } - - configNames = configNames.Distinct().ToList(); - platformNames = platformNames.Distinct().ToList(); - - result.Add(configNames); - result.Add(platformNames); return result; } public static DTE2 GetDTE2(DTE dte) { - List dte2List = new List(); - - IRunningObjectTable runningObjectTable = null; - GetRunningObjectTable(0, out runningObjectTable); - - IEnumMoniker enumMoniker = null; - runningObjectTable.EnumRunning(out enumMoniker); - - enumMoniker.Reset(); - IntPtr fetched = IntPtr.Zero; - IMoniker[] moniker = new IMoniker[1]; - - while (enumMoniker.Next(1, moniker, fetched) == 0) + try { - IBindCtx bindCtx = null; - CreateBindCtx(0, out bindCtx); + List dte2List = new List(); - string displayName = ""; - moniker[0].GetDisplayName(bindCtx, null, out displayName); - // add all VisualStudio ROT entries to list - if (displayName.StartsWith("!VisualStudio")) + IRunningObjectTable runningObjectTable = null; + GetRunningObjectTable(0, out runningObjectTable); + + IEnumMoniker enumMoniker = null; + runningObjectTable.EnumRunning(out enumMoniker); + + enumMoniker.Reset(); + IntPtr fetched = IntPtr.Zero; + IMoniker[] moniker = new IMoniker[1]; + + while (enumMoniker.Next(1, moniker, fetched) == 0) { - object comObject; - runningObjectTable.GetObject(moniker[0], out comObject); - dte2List.Add((DTE2)comObject); - } - } + IBindCtx bindCtx = null; + CreateBindCtx(0, out bindCtx); - // find the correct dte2 instance (each running VS instance has one...) - KeyValuePair maxMatch = new KeyValuePair(null, 0); - foreach(DTE2 dte2 in dte2List) - { - int m = StringUtility.GetMatchingCharsFromStart(dte.Solution.FullName, dte2.Solution.FullName); - if(m > maxMatch.Value) - { - maxMatch = new KeyValuePair(dte2, m); - } - } - - return maxMatch.Key; - } - - // returns true if the project was reloaded, false if the project did not need to be reloaded - static public Guid ReloadProject(Project project) - { - if (project != null && project.Kind == EnvDTE.Constants.vsProjectKindUnmodeled) - { - DTE dte = project.DTE; - - ServiceProvider sp = new ServiceProvider(dte as Microsoft.VisualStudio.OLE.Interop.IServiceProvider); - IVsSolution vsSolution = sp.GetService(typeof(SVsSolution)) as IVsSolution; - - IVsHierarchy hierarchy; - - string solutionDirectory = ""; - string solutionFile = ""; - string userOptions = ""; - vsSolution.GetSolutionInfo(out solutionDirectory, out solutionFile, out userOptions); - - vsSolution.GetProjectOfUniqueName(solutionDirectory + project.UniqueName, out hierarchy); - - if (hierarchy != null) - { - Guid projectGuid; - - hierarchy.GetGuidProperty( - VSConstants.VSITEMID_ROOT, - (int)__VSHPROPID.VSHPROPID_ProjectIDGuid, - out projectGuid); - - if (projectGuid != null) + string displayName = ""; + moniker[0].GetDisplayName(bindCtx, null, out displayName); + // add all VisualStudio ROT entries to list + if (displayName.StartsWith("!VisualStudio")) { - (vsSolution as IVsSolution4).ReloadProject(projectGuid); - return projectGuid; + object comObject; + runningObjectTable.GetObject(moniker[0], out comObject); + dte2List.Add((DTE2)comObject); } } + + // find the correct dte2 instance (each running VS instance has one...) + KeyValuePair maxMatch = new KeyValuePair(null, 0); + foreach (DTE2 dte2 in dte2List) + { + int m = StringUtility.GetMatchingCharsFromStart(dte.Solution.FullName, dte2.Solution.FullName); + if (m > maxMatch.Value) + { + maxMatch = new KeyValuePair(dte2, m); + } + } + + return maxMatch.Key; } - - return Guid.Empty; - } - - static public void UnloadProject(Guid guid, DTE dte) - { - if (dte == null) + catch(Exception e) { - return; + Logging.Logging.LogError("Exception: " + e.Message); + return null; } - - EnvDTE.Solution solution = dte.Solution; - - EnvDTE.Projects projects = solution.Projects; - - ServiceProvider sp = new ServiceProvider(dte as Microsoft.VisualStudio.OLE.Interop.IServiceProvider); - IVsSolution vsSolution = sp.GetService(typeof(SVsSolution)) as IVsSolution; - - (vsSolution as IVsSolution4).UnloadProject(guid, (uint)_VSProjectUnloadStatus.UNLOADSTATUS_UnloadedByUser); } static public bool ContainsCFiles(DTE dte) @@ -399,35 +379,44 @@ namespace CoatiSoftware.CoatiPlugin.Utility { List solutionProjects = new List(); - SolutionStructure solutionStructure = GetProjectStructureRecursive(dte); - - Stack nodeStack = new Stack(); - foreach (SolutionStructure.Node node in solutionStructure.Nodes) + try { - nodeStack.Push(node); - } + SolutionStructure solutionStructure = GetProjectStructureRecursive(dte); - while (nodeStack.Count > 0) - { - Utility.SolutionUtility.SolutionStructure.Node node = nodeStack.Pop(); - - string name = node.Name; - - if (node.GetNodeType() == Utility.SolutionUtility.SolutionStructure.Node.NodeType.PROJECT) + Stack nodeStack = new Stack(); + foreach (SolutionStructure.Node node in solutionStructure.Nodes) { - solutionProjects.Add(node.Project); + nodeStack.Push(node); } - else if (node.GetNodeType() == Utility.SolutionUtility.SolutionStructure.Node.NodeType.FOLDER) + + while (nodeStack.Count > 0) { - Utility.SolutionUtility.SolutionStructure.FolderNode folderNode = node as Utility.SolutionUtility.SolutionStructure.FolderNode; - foreach (Utility.SolutionUtility.SolutionStructure.Node subNode in folderNode.SubNodes) + Utility.SolutionUtility.SolutionStructure.Node node = nodeStack.Pop(); + + string name = node.Name; + + if (node.GetNodeType() == Utility.SolutionUtility.SolutionStructure.Node.NodeType.PROJECT) { - nodeStack.Push(subNode); + solutionProjects.Add(node.Project); + } + else if (node.GetNodeType() == Utility.SolutionUtility.SolutionStructure.Node.NodeType.FOLDER) + { + Utility.SolutionUtility.SolutionStructure.FolderNode folderNode = node as Utility.SolutionUtility.SolutionStructure.FolderNode; + foreach (Utility.SolutionUtility.SolutionStructure.Node subNode in folderNode.SubNodes) + { + nodeStack.Push(subNode); + } } } - } - return solutionProjects; + return solutionProjects; + } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); + + return new List(); + } } } } diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/SystemUtility.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/SystemUtility.cs index 358b44e7..95d10590 100644 --- a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/SystemUtility.cs +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Utility/SystemUtility.cs @@ -11,13 +11,20 @@ namespace CoatiSoftware.CoatiPlugin.Utility public static void GetWindowFocus() { - System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess(); - IntPtr windowHandle = process.MainWindowHandle; - - if (windowHandle != null) + try { - SetForegroundWindow(windowHandle); - SetActiveWindow(windowHandle); + System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess(); + IntPtr windowHandle = process.MainWindowHandle; + + if (windowHandle != null) + { + SetForegroundWindow(windowHandle); + SetActiveWindow(windowHandle); + } + } + catch(Exception e) + { + Logging.Logging.LogError("Exception: " + e.Message); } } } diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Wizard/ProjectSetupWindow.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Wizard/ProjectSetupWindow.cs index 6ca5ab73..d84dcaa1 100644 --- a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Wizard/ProjectSetupWindow.cs +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Wizard/ProjectSetupWindow.cs @@ -56,6 +56,8 @@ namespace CoatiSoftware.CoatiPlugin.Wizard public void UpdateGUI() { + Logging.Logging.LogInfo("Populating GUI"); + InitProjectCheckList(); InitComboBoxConfigurations(); InitComboBoxPlatforms(); @@ -66,6 +68,8 @@ namespace CoatiSoftware.CoatiPlugin.Wizard private void InitComboBoxConfigurations() { + Logging.Logging.LogInfo("Adding " + m_configurations.Count.ToString() + " build configurations."); + foreach(string configuration in m_configurations) { comboBoxConfiguration.Items.Add(configuration); @@ -79,7 +83,9 @@ namespace CoatiSoftware.CoatiPlugin.Wizard private void InitComboBoxPlatforms() { - foreach(string platform in m_platforms) + Logging.Logging.LogInfo("Adding " + m_platforms.Count.ToString() + " target platforms."); + + foreach (string platform in m_platforms) { comboBoxPlatform.Items.Add(platform); } @@ -141,6 +147,8 @@ namespace CoatiSoftware.CoatiPlugin.Wizard private void InitTextBoxTargetDirectory() { + Logging.Logging.LogInfo("Setting default target directory: \"" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(m_solutionDirectory) + "\""); + folderBrowserTargetDirectory.SelectedPath = m_solutionDirectory; string rootDirectory = folderBrowserTargetDirectory.SelectedPath.ToString(); textBoxTargetDirectory.Text = rootDirectory; @@ -148,20 +156,24 @@ namespace CoatiSoftware.CoatiPlugin.Wizard private void InitTextBoxFileName() { - textBoxFileName.Text = m_solutionFileName; + Logging.Logging.LogInfo("Setting default file name: '" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(m_solutionFileName) + "'"); - // MakeFileNameUnique(); + textBoxFileName.Text = m_solutionFileName; } private void InitComboBoxCStandard() { if(m_containsCFiles == false) { + Logging.Logging.LogInfo("Hiding C Standard selection"); + comboBoxCStandard.Hide(); labelCStandard.Hide(); } else { + Logging.Logging.LogInfo("Showing C Standard selection"); + comboBoxCStandard.Show(); labelCStandard.Show(); @@ -176,6 +188,8 @@ namespace CoatiSoftware.CoatiPlugin.Wizard private void buttonCancel_Click(object sender, EventArgs e) { + Logging.Logging.LogInfo("Close button pressed. Aborting."); + Close(); } @@ -186,6 +200,8 @@ namespace CoatiSoftware.CoatiPlugin.Wizard private void OnCreate() { + Logging.Logging.LogInfo("Create button pressed"); + if (m_onCreateProject != null) { string configurationName = ""; @@ -194,16 +210,21 @@ namespace CoatiSoftware.CoatiPlugin.Wizard configurationName = comboBoxConfiguration.SelectedItem as string; platformName = comboBoxPlatform.SelectedItem as string; + Logging.Logging.LogInfo("Configuration " + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(configurationName) + "|" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(platformName) + " was selected."); + string targetDir = textBoxTargetDirectory.Text; if(Directory.Exists(targetDir) && CheckFileNameIsValid(textBoxFileName.Text)) { if (CheckFileExists()) { + Logging.Logging.LogWarning("A file \"" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(targetDir + "\\" + textBoxFileName.Text) + "\" already exists."); + DialogResult result = MessageBox.Show("A file of the chosen name already exists. Do you want to replace it?", "Coati Plugin", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if(result == DialogResult.No) { + Logging.Logging.LogInfo("Aborting CDB creation and attempting to make file name unique."); MakeFileNameUnique(); return; } @@ -215,6 +236,8 @@ namespace CoatiSoftware.CoatiPlugin.Wizard cStandard = comboBoxCStandard.SelectedItem as string; } + Logging.Logging.LogInfo("Setting C standard flag to " + cStandard); + m_onCreateProject(GetTreeViewProjectItems(), configurationName, platformName, targetDir, textBoxFileName.Text, cStandard); Close(); } @@ -222,15 +245,21 @@ namespace CoatiSoftware.CoatiPlugin.Wizard { if(Directory.Exists(targetDir) == false) { + Logging.Logging.LogError("The target directory \"" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(targetDir) + "\" does not exist."); MessageBox.Show("The target directory does not exist.", "Coati Plugin", MessageBoxButtons.OK, MessageBoxIcon.Warning); } if (CheckFileNameIsValid(textBoxFileName.Text) == false) { + Logging.Logging.LogError("The chosen file name \"" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(textBoxFileName.Text) + "\" is not valid. I'd almost dare to say it's invalid!"); MessageBox.Show("The chosen file name is not valid.", "Coati Plugin", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } } + else + { + Logging.Logging.LogError("CDB create callback is not set. Cannot start creating CDB."); + } } private bool CheckFileNameIsValid(string fileName) diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Wizard/WindowCreateCDB.cs b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Wizard/WindowCreateCDB.cs index bb7fe1f6..b85c50f8 100644 --- a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Wizard/WindowCreateCDB.cs +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/Wizard/WindowCreateCDB.cs @@ -12,7 +12,15 @@ namespace CoatiSoftware.CoatiPlugin.Wizard { public partial class WindowCreateCDB : Form { - public delegate void OnFinishedCreatingCDB(SolutionParser.CompilationDatabase cdb, string directory, string fileName); + public struct CreationResult + { + public SolutionParser.CompilationDatabase _cdb; + public string _cdbDirectory; + public string _cdbName; + public List _headerDirectories; + } + + public delegate void OnFinishedCreatingCDB(CreationResult result); private OnFinishedCreatingCDB m_onFinishedCreateCDB = null; @@ -24,7 +32,7 @@ namespace CoatiSoftware.CoatiPlugin.Wizard string m_fileName = ""; string m_cStandard = ""; - SolutionParser.CompilationDatabase m_cdb = null; + CreationResult m_result = new CreationResult(); public OnFinishedCreatingCDB CallbackOnFinishedCreatingCDB { @@ -86,18 +94,30 @@ namespace CoatiSoftware.CoatiPlugin.Wizard backgroundWorker1.RunWorkerAsync(); } - private SolutionParser.CompilationDatabase CreateCDB() + private CreationResult CreateCDB() { + CreationResult result = new CreationResult(); + result._cdb = null; + result._cdbDirectory = ""; + result._cdbName = ""; + result._headerDirectories = new List(); + + Logging.Logging.LogInfo("Starting to create CDB"); + SolutionParser.CompilationDatabase cdb = new SolutionParser.CompilationDatabase(); int projectsProcessed = 0; + List headerDirectories = new List(); + SolutionParser.SolutionParser._headerDirectories.Clear(); + foreach (EnvDTE.Project project in m_projects) { List commandObjects = SolutionParser.SolutionParser.CreateCommandObjects(project, m_configurationName, m_platformName, m_cStandard); projectsProcessed++; float relativProgress = (float)projectsProcessed/(float)m_projects.Count; + Logging.Logging.LogInfo("Processing project \"" + Logging.Obfuscation.NameObfuscator.GetObfuscatedName(project.Name) + "\""); backgroundWorker1.ReportProgress((int)(relativProgress * 100), "Processing project \"" + project.Name + "\""); foreach (SolutionParser.CommandObject obj in commandObjects) @@ -106,30 +126,52 @@ namespace CoatiSoftware.CoatiPlugin.Wizard } } - return cdb; + headerDirectories = SolutionParser.SolutionParser._headerDirectories; + + result._cdb = cdb; + result._cdbDirectory = m_targetDir; + result._cdbName = m_fileName; + result._headerDirectories = headerDirectories; + + Logging.Logging.LogInfo("Done creating CDB"); + + return result; } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { - m_cdb = CreateCDB(); + m_result = CreateCDB(); } private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { - progressBar.Value = e.ProgressPercentage; - - labelStatus.Text = e.UserState as string; + if(backgroundWorker1.CancellationPending == false) + { + progressBar.Value = e.ProgressPercentage; + + labelStatus.Text = e.UserState as string; + } + else + { + progressBar.Value = 0; + + labelStatus.Text = "Cancelling..."; + } } private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { - if(e.Cancelled == false && e.Error == null) + if(e.Cancelled == false && e.Error == null && progressBar.Value >= 100) { if (m_onFinishedCreateCDB != null) { - m_onFinishedCreateCDB(m_cdb, m_targetDir, m_fileName); + m_onFinishedCreateCDB(m_result); } } + else + { + Logging.Logging.LogWarning("CDB creation was aborted by user"); + } Close(); } diff --git a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/source.extension.vsixmanifest b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/source.extension.vsixmanifest index 9ed6f464..7e0e4380 100644 --- a/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/source.extension.vsixmanifest +++ b/ide_plugins/vs/vs2015/CoatiPlugin/CoatiPlugin/source.extension.vsixmanifest @@ -1,7 +1,7 @@  - + CoatiPlugin This package allows Coati to communicate with Visual Studio and vice versa. https://www.coati.io/ diff --git a/web/documentation/img/vs_log_folder.png b/web/documentation/img/vs_log_folder.png new file mode 100644 index 00000000..672dfbb1 Binary files /dev/null and b/web/documentation/img/vs_log_folder.png differ diff --git a/web/documentation/img/vs_output_window.png b/web/documentation/img/vs_output_window.png new file mode 100644 index 00000000..0b38bf83 Binary files /dev/null and b/web/documentation/img/vs_output_window.png differ diff --git a/web/documentation/img/vs_plugin_options.png b/web/documentation/img/vs_plugin_options.png new file mode 100644 index 00000000..2783dc2b Binary files /dev/null and b/web/documentation/img/vs_plugin_options.png differ diff --git a/web/documentation/img/vs_plugin_ports_0.png b/web/documentation/img/vs_plugin_ports_0.png new file mode 100644 index 00000000..4930546b Binary files /dev/null and b/web/documentation/img/vs_plugin_ports_0.png differ diff --git a/web/documentation/img/vs_plugin_ports_1.png b/web/documentation/img/vs_plugin_ports_1.png new file mode 100644 index 00000000..28e14b93 Binary files /dev/null and b/web/documentation/img/vs_plugin_ports_1.png differ diff --git a/web/documentation/index.html b/web/documentation/index.html index f0583e73..2668963e 100644 --- a/web/documentation/index.html +++ b/web/documentation/index.html @@ -1856,7 +1856,61 @@

Once the CDB was successfully created it can be found in the specified target directory. From this CDB you can create a Coati project as described above.

+ + Settings +

Network settings and logging options can be changed in the plugin's Tools/Options entry.

+
+
+ +
+
+ +

+ +
+
+ +
+
+ +

+ + + + + + + + + + + +
Option Description
Coati Port The port on which Coati will receive messages. Note that this must match the port setting in Coati itself.
VS Port The port on which Visual Studio will receive messages. Note that this must match the port setting in Coati itself.
File Logging Enable log output for the plugin. Additionaly to the output file, log messages will also be displayed in VS'
Log Obfuscation Obfuscate project- and file names as well as directories in the log output. Note that already logged data will not be obfuscated retroactively. A dictionary, mapping obfuscated names to original names, will be created in a seperate file.
+ Logging + +

The plugin offers optional file logging. Should you ever have problems with the plugin we recommend to turn logging on. This will help to pinpoint and resolve the issue faster.

+

Logs will be created in ..\AppData\Local\Coati Software\Plugins\VS folder. A new log file will be created every time you restart VS and logging is enabled.

+ +
+
+ +
+
+ + +

Note that the logs will include project- and file names as well as directories specific to your project. If you wish to keep this informatin secret you can use log obfuscation.

+

Project- and file names as well as directories will be replaced by an alphanumeric sequence. The sequence has the form a0, b0, c0,..., a1, b1, c1,.... Note that after switching on obfuscation, already logged data will not be obfuscated retroactively. No log files will be send to Coati Software automatically. You can check any file you may want to send us for sensible information before sending it.

+

A dictionary, mapping obfuscated names to original names, will be created in your log folder if log obfuscation is switched on. When during the support process we refer to project items by their obfuscated name you can still make sense of it. Do not send the dictionary to anybody else.

+ + +

Lastly, log messages are also displayed in the VS output window. This is tied to file logging and is not en- or disabled separately.

+
+
+ +
+
+