From 36e81819552cc8776a319f9720ce023fb1116a18 Mon Sep 17 00:00:00 2001 From: Anders Bakken Date: Thu, 23 Mar 2017 23:01:17 -0700 Subject: [PATCH] Fix issue #908. Return predefined error codes for certain well known errors. --- src/RClient.cpp | 36 ++++++++++++++++++++------------- src/RClient.h | 4 +++- src/RTags.h | 12 +++++++++++ src/Server.cpp | 40 +++++++++++++++++++------------------ src/rc.cpp | 6 ++---- src/rtags.el | 53 +++++++++++++++++++++++++++---------------------- 6 files changed, 89 insertions(+), 62 deletions(-) diff --git a/src/RClient.cpp b/src/RClient.cpp index 141a9f504..99e5163e2 100644 --- a/src/RClient.cpp +++ b/src/RClient.cpp @@ -320,7 +320,7 @@ RClient::RClient() : mMax(-1), mTimeout(-1), mMinOffset(-1), mMaxOffset(-1), mConnectTimeout(DEFAULT_CONNECT_TIMEOUT), mBuildIndex(0), mLogLevel(LogLevel::Error), mTcpPort(0), mGuessFlags(false), - mTerminalWidth(-1) + mTerminalWidth(-1), mExitCode(RTags::ArgumentParseError) { struct winsize w; ioctl(0, TIOCGWINSZ, &w); @@ -362,7 +362,7 @@ void RClient::addCompile(Path &&path) mCommands.append(std::make_shared(std::move(path))); } -int RClient::exec() +void RClient::exec() { RTags::initMessages(); OnDestruction onDestruction([]() { Message::cleanup(); }); @@ -379,7 +379,8 @@ int RClient::exec() if (!connection->connectTcp(mTcpHost, mTcpPort, mConnectTimeout)) { if (mLogLevel >= LogLevel::Error) fprintf(stdout, "Can't seem to connect to server (%s:%d)\n", mTcpHost.constData(), mTcpPort); - return 1; + mExitCode = RTags::ConnectionFailure; + return; } connection->connected().connect(std::bind(&EventLoop::quit, loop.get())); loop->exec(mConnectTimeout); @@ -391,32 +392,31 @@ int RClient::exec() fprintf(stdout, "Can't seem to connect to server (%s)\n", mSocketFile.constData()); } } - return 1; + mExitCode = RTags::ConnectionFailure; + return; } } else if (!connection->connectUnix(mSocketFile, mConnectTimeout)) { if (mLogLevel >= LogLevel::Error) fprintf(stdout, "Can't seem to connect to server (%s)\n", mSocketFile.constData()); - return 1; + mExitCode = RTags::ConnectionFailure; + return; } - int ret = 0; - bool hasZeroExit = false; for (int i=0; i &cmd = mCommands.at(i); debug() << "running command " << cmd->description(); - if (!cmd->exec(this, connection) || loop->exec(timeout()) != EventLoop::Success) { - ret = 1; + if (!cmd->exec(this, connection)) { + mExitCode = RTags::NetworkFailure; + break; + } else if (loop->exec(timeout()) != EventLoop::Success) { + mExitCode = RTags::TimeoutFailure; break; } - if (connection->finishStatus() == 0) - hasZeroExit = true; + mExitCode = connection->finishStatus(); } if (connection->client()) connection->client()->close(); mCommands.clear(); - if (!ret && !hasZeroExit) - ret = connection->finishStatus(); - return ret; } CommandLineParser::ParseStatus RClient::parse(size_t argc, char **argv) @@ -457,9 +457,11 @@ CommandLineParser::ParseStatus RClient::parse(size_t argc, char **argv) break; } case Help: { CommandLineParser::help(stdout, "rc", opts); + mExitCode = RTags::Success; return { String(), CommandLineParser::Parse_Ok } ; } case Man: { CommandLineParser::man(opts); + mExitCode = RTags::Success; return { String(), CommandLineParser::Parse_Ok }; } case SocketFile: { mSocketFile = std::move(value); @@ -629,11 +631,13 @@ CommandLineParser::ParseStatus RClient::parse(size_t argc, char **argv) break; } case Version: { fprintf(stdout, "%s\n", RTags::versionString().constData()); + mExitCode = RTags::Success; return { String(), CommandLineParser::Parse_Ok }; } case VerifyVersion: { const int version = strtoul(value.constData(), 0, 10); if (version != NumOptions) { fprintf(stdout, "Protocol version mismatch\n"); + mExitCode = RTags::ProtocolFailure; return { String(), CommandLineParser::Parse_Error }; } break; } @@ -839,10 +843,12 @@ CommandLineParser::ParseStatus RClient::parse(size_t argc, char **argv) case FindProjectRoot: { const Path p = Path::resolved(value); // this won't work correctly with --no-realpath unless --no-realpath is passed first printf("findProjectRoot [%s] => [%s]\n", p.constData(), RTags::findProjectRoot(p, RTags::SourceRoot).constData()); + mExitCode = RTags::Success; return { String(), CommandLineParser::Parse_Ok }; } case FindProjectBuildRoot: { const Path p = Path::resolved(value); // this won't work correctly with --no-realpath unless --no-realpath is passed first printf("findProjectRoot [%s] => [%s]\n", p.constData(), RTags::findProjectRoot(p, RTags::BuildRoot).constData()); + mExitCode = RTags::Success; return { String(), CommandLineParser::Parse_Ok }; } case RTagsConfig: { const Path p = Path::resolved(value); // this won't work correctly with --no-realpath unless --no-realpath is passed first @@ -851,6 +857,7 @@ CommandLineParser::ParseStatus RClient::parse(size_t argc, char **argv) for (const auto &it : config) { printf("%s: \"%s\"\n", it.first.constData(), it.second.constData()); } + mExitCode = RTags::Success; return { String(), CommandLineParser::Parse_Ok }; } case CurrentProject: { addQuery(QueryMessage::Project, String(), QueryMessage::CurrentProjectOnly); @@ -952,6 +959,7 @@ CommandLineParser::ParseStatus RClient::parse(size_t argc, char **argv) print(CXCursor_FirstAttr, CXCursor_LastAttr); Log(LogLevel::Error, LogOutput::StdOut | LogOutput::TrailingNewLine) << "Preprocessing:"; print(CXCursor_FirstPreprocessing, CXCursor_LastPreprocessing); + mExitCode = RTags::Success; return { String(), CommandLineParser::Parse_Ok }; } case SetBuffers: { String arg; diff --git a/src/RClient.h b/src/RClient.h index 115eef45e..dd382ba00 100644 --- a/src/RClient.h +++ b/src/RClient.h @@ -159,7 +159,8 @@ class RClient RClient(); ~RClient(); - int exec(); + void exec(); + int exitCode() const { return mExitCode; } CommandLineParser::ParseStatus parse(size_t argc, char **argv); int max() const { return mMax; } @@ -217,6 +218,7 @@ class RClient bool mGuessFlags; Path mProjectRoot; int mTerminalWidth; + int mExitCode; #ifdef RTAGS_HAS_LUA List mVisitASTScripts; #endif diff --git a/src/RTags.h b/src/RTags.h index 402152174..024bc66bb 100644 --- a/src/RTags.h +++ b/src/RTags.h @@ -96,6 +96,18 @@ String versionString(); const LogLevel DiagnosticsLevel(-2); const LogLevel Statistics(-3); +enum ExitCodes { + Success = 0, + GeneralFailure = 32, + NetworkFailure = 33, + TimeoutFailure = 34, + NotIndexed = 35, + ConnectionFailure = 36, + ProtocolFailure = 37, + ArgumentParseError = 38, + UnexpectedMessageError = 39, + UnknownMessageError = 40 +}; enum UnitType { CompileC, CompileCPlusPlus diff --git a/src/Server.cpp b/src/Server.cpp index 4a9f2f0ee..d5b9c3296 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -388,11 +388,11 @@ void Server::onNewMessage(const std::shared_ptr &message, const std::sh case VisitFileResponseMessage::MessageId: error() << "Unexpected message" << static_cast(message->messageId()); // assert(0); - connection->finish(1); + connection->finish(RTags::UnexpectedMessageError); break; default: error("Unknown message: %d", message->messageId()); - connection->finish(1); + connection->finish(RTags::UnknownMessageError); break; } if ((mOptions.options & (NoFileManagerWatch|NoFileManager)) == NoFileManagerWatch) { @@ -776,14 +776,14 @@ void Server::followLocation(const std::shared_ptr &query, const st const Location loc = query->location(); if (loc.isNull()) { conn->write("Not indexed"); - conn->finish(1); + conn->finish(RTags::NotIndexed); return; } std::shared_ptr project = projectForQuery(query); if (!project) { error("No project"); conn->write("Not indexed"); - conn->finish(1); + conn->finish(RTags::NotIndexed); return; } @@ -792,7 +792,7 @@ void Server::followLocation(const std::shared_ptr &query, const st { FollowLocationJob job(loc, query, project); if (!job.run(conn)) { - conn->finish(0); + conn->finish(); return; } } @@ -815,7 +815,7 @@ void Server::followLocation(const std::shared_ptr &query, const st if (path.startsWith(projectPath)) { FollowLocationJob job(loc, query, proj.second); if (job.run(conn)) { - conn->finish(0); + conn->finish(); return; } } @@ -825,8 +825,10 @@ void Server::followLocation(const std::shared_ptr &query, const st } if (!project->dependencies().contains(loc.fileId())) { conn->write("Not indexed"); + conn->finish(RTags::NotIndexed); + } else { + conn->finish(RTags::GeneralFailure); } - conn->finish(1); } void Server::isIndexing(const std::shared_ptr &, const std::shared_ptr &conn) @@ -898,7 +900,7 @@ void Server::startClangThread(const std::shared_ptr &query, const if (!project->dependencies().contains(fileId)) { conn->write("Not indexed"); - conn->finish(1); + conn->finish(RTags::NotIndexed); return; } @@ -1068,7 +1070,7 @@ void Server::symbolInfo(const std::shared_ptr &query, const std::s fileId = Location::fileId(path); if (!fileId) { conn->write("Not indexed"); - conn->finish(1); + conn->finish(RTags::NotIndexed); return; } } @@ -1076,7 +1078,7 @@ void Server::symbolInfo(const std::shared_ptr &query, const std::s std::shared_ptr project = projectForQuery(query); if (!project || !project->dependencies().contains(fileId)) { conn->write("Not indexed"); - conn->finish(1); + conn->finish(RTags::NotIndexed); return; } @@ -1150,7 +1152,7 @@ void Server::referencesForLocation(const std::shared_ptr &query, c const Location loc = query->location(); if (loc.isNull()) { conn->write("Not indexed"); - conn->finish(); + conn->finish(RTags::NotIndexed); return; } std::shared_ptr project = projectForQuery(query); @@ -1158,7 +1160,7 @@ void Server::referencesForLocation(const std::shared_ptr &query, c if (!project) { error("No project"); conn->write("Not indexed"); - conn->finish(); + conn->finish(RTags::NotIndexed); return; } @@ -1166,7 +1168,7 @@ void Server::referencesForLocation(const std::shared_ptr &query, c if (!project->dependencies().contains(loc.fileId())) { conn->write("Not indexed"); - conn->finish(1); + conn->finish(RTags::NotIndexed); return; } @@ -1272,7 +1274,7 @@ void Server::reloadFileManager(const std::shared_ptr &query, const if (project) { if (mOptions.options & NoFileManager) { conn->write<512>("Not watching files"); - conn->finish(1); + conn->finish(RTags::GeneralFailure); } else { conn->write<512>("Reloading files for %s", project->path().constData()); conn->finish(); @@ -1714,7 +1716,7 @@ void Server::dumpCompileCommands(const std::shared_ptr &query, con project = currentProject(); if (!project) { conn->write("No current project"); - conn->finish(1); + conn->finish(RTags::GeneralFailure); return; } @@ -1846,20 +1848,20 @@ void Server::classHierarchy(const std::shared_ptr &query, const st const Location loc = query->location(); if (loc.isNull()) { conn->write("Not indexed"); - conn->finish(1); + conn->finish(RTags::NotIndexed); return; } std::shared_ptr project = projectForQuery(query); if (!project) { error("No project"); conn->write("Not indexed"); - conn->finish(1); + conn->finish(RTags::NotIndexed); return; } if (!project->dependencies().contains(loc.fileId())) { conn->write("Not indexed"); - conn->finish(1); + conn->finish(RTags::NotIndexed); return; } @@ -1930,7 +1932,7 @@ void Server::validate(const std::shared_ptr &query, const std::sha if (!project) { error("No project"); conn->write("No current project"); - conn->finish(1); + conn->finish(RTags::GeneralFailure); return; } diff --git a/src/rc.cpp b/src/rc.cpp index 6d7d50cbe..c34a50f87 100644 --- a/src/rc.cpp +++ b/src/rc.cpp @@ -18,18 +18,16 @@ int main(int argc, char** argv) { RClient rc; - int ret = 0; const CommandLineParser::ParseStatus status = rc.parse(argc, argv); switch (status.status) { case CommandLineParser::Parse_Ok: break; case CommandLineParser::Parse_Error: - ret = 1; fprintf(stderr, "%s\n", status.error.constData()); break; case CommandLineParser::Parse_Exec: - ret = rc.exec(); + rc.exec(); break; } - return ret; + return rc.exitCode(); } diff --git a/src/rtags.el b/src/rtags.el index ce5935d40..158ee6a3b 100644 --- a/src/rtags.el +++ b/src/rtags.el @@ -77,6 +77,17 @@ (defconst rtags-diagnostics-buffer-name "*RTags Diagnostics*") (defconst rtags-diagnostics-raw-buffer-name " *RTags Raw*") +(defconst rtags-exit-code-success 0) +(defconst rtags-exit-code-general-failure 32) +(defconst rtags-exit-code-network-failure 33) +(defconst rtags-exit-code-timeout-failure 34) +(defconst rtags-exit-code-not-indexed 35) +(defconst rtags-exit-code-connection-failure 36) +(defconst rtags-exit-code-protocol-failure 37) +(defconst rtags-exit-code-argument-parse-error 38) +(defconst rtags-return-value-unexpected-message-error 39) +(defconst rtags-return-value-unknown-message-error 40) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Variables @@ -1205,30 +1216,24 @@ to only call this when `rtags-socket-file' is defined. ;; synchronous (goto-char (point-min)) (save-excursion - (and (cond ((re-search-forward "^Can't seem to connect to server" nil t) - (erase-buffer) - (setq rtags-last-request-not-connected t) - (unless noerror - (error "Can't seem to connect to server. Is rdm running?")) - nil) - ((re-search-forward "^Protocol version mismatch" nil t) - (erase-buffer) - (unless noerror - (error (concat "RTags protocol version mismatch. This is usually caused by getting rtags.el from melpa\n" - "and installing a new rtags build that modified the protocol. They need to be in sync."))) - nil) - ((re-search-forward "^Not indexed" nil t) - (erase-buffer) - (setq rtags-last-request-not-indexed t) - nil) - ((looking-at "Project loading") - (erase-buffer) - (message "Project loading...") - t) - (t)) - (eq result 0) - rtags-autostart-diagnostics (rtags-diagnostics))))) - (or async (> (point-max) (point-min))))))) + (cond ((= result rtags-exit-code-success) + (when rtags-autostart-diagnostics + (rtags-diagnostics))) + ((= result rtags-exit-code-connection-failure) + (erase-buffer) + (setq rtags-last-request-not-connected t) + (unless noerror + (error "Can't seem to connect to server. Is rdm running?"))) + ((= result rtags-exit-code-protocol-failure) + (erase-buffer) + (unless noerror + (error (concat "RTags protocol version mismatch. This is usually caused by getting rtags.el from melpa\n" + "and installing a new rtags build that modified the protocol. They need to be in sync.")))) + ((= result rtags-exit-code-not-indexed) + (erase-buffer) + (setq rtags-last-request-not-indexed t)) + (t)))) ;; other error + (or async (and (> (point-max) (point-min)) (= result rtags-exit-code-success)))))))) (defvar rtags-preprocess-mode-map (make-sparse-keymap)) (define-key rtags-preprocess-mode-map (kbd "q") 'rtags-call-bury-or-delete)