diff --git a/CMakeLists.txt b/CMakeLists.txt index 67f2a9910d..d895744b4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ endif () set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # where to find CMAKE modules -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) # be verbose about flags used option(VERBOSE OFF) diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index e3ef360b83..88f5a24706 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -193,7 +193,7 @@ add_executable(${BIN_ARANGOD} GeneralServer/GeneralServer.cpp GeneralServer/GeneralServerFeature.cpp GeneralServer/HttpCommTask.cpp - GeneralServer/HttpServerJob.cpp + GeneralServer/GeneralServerJob.cpp GeneralServer/HttpsCommTask.cpp GeneralServer/PathHandler.cpp GeneralServer/RestHandler.cpp diff --git a/arangod/Cluster/ClusterComm.cpp b/arangod/Cluster/ClusterComm.cpp index 4198d653b8..25f4b3eefb 100644 --- a/arangod/Cluster/ClusterComm.cpp +++ b/arangod/Cluster/ClusterComm.cpp @@ -945,9 +945,8 @@ void ClusterComm::asyncAnswer(std::string& coordinatorHeader, // We add this result to the operation struct without acquiring // a lock, since we know that only we do such a thing: - std::unique_ptr result( - client->request(rest::RequestType::PUT, "/_api/shard-comm", - body, len, headers)); + std::unique_ptr result(client->request( + rest::RequestType::PUT, "/_api/shard-comm", body, len, headers)); if (result.get() == nullptr || !result->isComplete()) { cm->brokenConnection(connection); client->invalidateConnection(); @@ -1273,9 +1272,8 @@ size_t ClusterComm::performRequests(std::vector& requests, requests[index].result = res; // In this case we will retry: - dueTime[index] = (std::min)(10.0, - (std::max)(0.2, 2 * (now - startTime))) + - now; + dueTime[index] = + (std::min)(10.0, (std::max)(0.2, 2 * (now - startTime))) + now; if (dueTime[index] >= endTime) { requests[index].done = true; nrDone++; @@ -1362,6 +1360,13 @@ size_t ClusterComm::performSingleRequest( rest::ContentType type = rest::ContentType::JSON; basics::StringBuffer& buffer = req.result.result->getBody(); + + // PERFORMANCE TODO (fc) (max) (obi) + // body() could return a basic_string_ref + + // The FakeRequest Replacement does a copy of the body and is not as fast + // as the original + // auto answer = new FakeRequest(type, buffer.c_str(), // static_cast(buffer.length())); // answer->setHeaders(req.result.result->getHeaderFields()); @@ -1371,8 +1376,8 @@ size_t ClusterComm::performSingleRequest( req.result.result->getHeaderFields()); req.result.answer.reset(static_cast(answer)); - req.result.answer_code = static_cast( - req.result.result->getHttpReturnCode()); + req.result.answer_code = + static_cast(req.result.result->getHttpReturnCode()); return (req.result.answer_code == rest::ResponseCode::OK || req.result.answer_code == rest::ResponseCode::CREATED || req.result.answer_code == rest::ResponseCode::ACCEPTED) @@ -1443,17 +1448,18 @@ void ClusterCommThread::run() { } } else { if (nullptr != op->body.get()) { - LOG(DEBUG) << "sending " - << arangodb::HttpRequest::translateMethod(op->reqtype) - .c_str() << " request to DB server '" - << op->result.serverID << "' at endpoint '" - << op->result.endpoint << "': " << op->body->c_str(); + LOG(DEBUG) + << "sending " + << arangodb::HttpRequest::translateMethod(op->reqtype).c_str() + << " request to DB server '" << op->result.serverID + << "' at endpoint '" << op->result.endpoint + << "': " << op->body->c_str(); } else { - LOG(DEBUG) << "sending " - << arangodb::HttpRequest::translateMethod(op->reqtype) - .c_str() << " request to DB server '" - << op->result.serverID << "' at endpoint '" - << op->result.endpoint << "'"; + LOG(DEBUG) + << "sending " + << arangodb::HttpRequest::translateMethod(op->reqtype).c_str() + << " request to DB server '" << op->result.serverID + << "' at endpoint '" << op->result.endpoint << "'"; } auto client = diff --git a/arangod/GeneralServer/AsyncJobManager.cpp b/arangod/GeneralServer/AsyncJobManager.cpp index e231a9248b..4b8ffb9d22 100644 --- a/arangod/GeneralServer/AsyncJobManager.cpp +++ b/arangod/GeneralServer/AsyncJobManager.cpp @@ -25,7 +25,7 @@ #include "Basics/ReadLocker.h" #include "Basics/WriteLocker.h" -#include "GeneralServer/HttpServerJob.h" +#include "GeneralServer/GeneralServerJob.h" #include "GeneralServer/RestHandler.h" #include "Logger/Logger.h" #include "Rest/GeneralResponse.h" @@ -260,7 +260,7 @@ std::vector AsyncJobManager::byStatus( /// @brief initializes an async job //////////////////////////////////////////////////////////////////////////////// -void AsyncJobManager::initAsyncJob(HttpServerJob* job, char const* hdr) { +void AsyncJobManager::initAsyncJob(GeneralServerJob* job, char const* hdr) { AsyncCallbackContext* ctx = nullptr; if (hdr != nullptr) { diff --git a/arangod/GeneralServer/AsyncJobManager.h b/arangod/GeneralServer/AsyncJobManager.h index 0f56606495..3a238494a6 100644 --- a/arangod/GeneralServer/AsyncJobManager.h +++ b/arangod/GeneralServer/AsyncJobManager.h @@ -32,7 +32,7 @@ class GeneralResponse; namespace rest { class AsyncCallbackContext; -class HttpServerJob; +class GeneralServerJob; //////////////////////////////////////////////////////////////////////////////// /// @brief AsyncJobResult @@ -133,7 +133,7 @@ class AsyncJobManager { /// @brief initializes an async job ////////////////////////////////////////////////////////////////////////////// - void initAsyncJob(HttpServerJob*, char const*); + void initAsyncJob(GeneralServerJob*, char const*); ////////////////////////////////////////////////////////////////////////////// /// @brief finishes the execution of an async job diff --git a/arangod/GeneralServer/GeneralServer.cpp b/arangod/GeneralServer/GeneralServer.cpp index 56eef214d7..ef0bcc797e 100644 --- a/arangod/GeneralServer/GeneralServer.cpp +++ b/arangod/GeneralServer/GeneralServer.cpp @@ -33,7 +33,7 @@ #include "GeneralServer/GeneralCommTask.h" #include "GeneralServer/GeneralListenTask.h" #include "GeneralServer/GeneralServerFeature.h" -#include "GeneralServer/HttpServerJob.h" +#include "GeneralServer/GeneralServerJob.h" #include "GeneralServer/RestHandler.h" #include "Logger/Logger.h" #include "Scheduler/ListenTask.h" @@ -127,13 +127,13 @@ bool GeneralServer::handleRequestAsync(GeneralCommTask* task, // execute the handler using the dispatcher std::unique_ptr job = - std::make_unique(this, handler, true); + std::make_unique(this, std::move(handler), true); task->RequestStatisticsAgent::transferTo(job.get()); // register the job with the job manager if (jobId != nullptr) { GeneralServerFeature::JOB_MANAGER->initAsyncJob( - static_cast(job.get()), hdr); + static_cast(job.get()), hdr); *jobId = job->jobId(); } @@ -171,11 +171,12 @@ bool GeneralServer::handleRequest(GeneralCommTask* task, bool startThread = handler->needsOwnThread(); // use a dispatcher queue, handler belongs to the job - std::unique_ptr job = std::make_unique(this, handler); + std::unique_ptr job = + std::make_unique(this, std::move(handler)); task->RequestStatisticsAgent::transferTo(job.get()); - LOG(TRACE) << "GeneralCommTask " << (void*)task << " created HttpServerJob " - << (void*)job.get(); + LOG(TRACE) << "GeneralCommTask " << (void*)task + << " created GeneralServerJob " << (void*)job.get(); // add the job to the dispatcher int res = DispatcherFeature::DISPATCHER->addJob(job, startThread); diff --git a/arangod/GeneralServer/HttpServerJob.cpp b/arangod/GeneralServer/GeneralServerJob.cpp similarity index 84% rename from arangod/GeneralServer/HttpServerJob.cpp rename to arangod/GeneralServer/GeneralServerJob.cpp index 772afb06fc..104f042684 100644 --- a/arangod/GeneralServer/HttpServerJob.cpp +++ b/arangod/GeneralServer/GeneralServerJob.cpp @@ -22,7 +22,7 @@ /// @author Achim Brandt //////////////////////////////////////////////////////////////////////////////// -#include "HttpServerJob.h" +#include "GeneralServerJob.h" #include "Basics/WorkMonitor.h" #include "Dispatcher/DispatcherQueue.h" @@ -42,28 +42,29 @@ using namespace arangodb::rest; /// @brief constructs a new server job //////////////////////////////////////////////////////////////////////////////// -HttpServerJob::HttpServerJob(GeneralServer* server, - WorkItem::uptr& handler, bool isAsync) - : Job("HttpServerJob"), +GeneralServerJob::GeneralServerJob(GeneralServer* server, + WorkItem::uptr handler, + bool isAsync) + : Job("GeneralServerJob"), _server(server), _workDesc(nullptr), _isAsync(isAsync) { - _handler.swap(handler); + _handler = std::move(handler); } //////////////////////////////////////////////////////////////////////////////// /// @brief destructs a server job //////////////////////////////////////////////////////////////////////////////// -HttpServerJob::~HttpServerJob() { +GeneralServerJob::~GeneralServerJob() { if (_workDesc != nullptr) { WorkMonitor::freeWorkDescription(_workDesc); } } -size_t HttpServerJob::queue() const { return _handler->queue(); } +size_t GeneralServerJob::queue() const { return _handler->queue(); } -void HttpServerJob::work() { +void GeneralServerJob::work() { TRI_ASSERT(_handler.get() != nullptr); RequestStatisticsAgent::transferTo(_handler.get()); @@ -106,13 +107,13 @@ void HttpServerJob::work() { _workDesc = WorkMonitor::popHandler(_handler.release(), false); } -bool HttpServerJob::cancel() { return _handler->cancel(); } +bool GeneralServerJob::cancel() { return _handler->cancel(); } -void HttpServerJob::cleanup(DispatcherQueue* queue) { +void GeneralServerJob::cleanup(DispatcherQueue* queue) { queue->removeJob(this); delete this; } -void HttpServerJob::handleError(arangodb::basics::Exception const& ex) { +void GeneralServerJob::handleError(arangodb::basics::Exception const& ex) { _handler->handleError(ex); } diff --git a/arangod/GeneralServer/HttpServerJob.h b/arangod/GeneralServer/GeneralServerJob.h similarity index 85% rename from arangod/GeneralServer/HttpServerJob.h rename to arangod/GeneralServer/GeneralServerJob.h index e4a6ae388f..1f1376b8af 100644 --- a/arangod/GeneralServer/HttpServerJob.h +++ b/arangod/GeneralServer/GeneralServerJob.h @@ -35,15 +35,15 @@ namespace rest { class RestHandler; class GeneralServer; -class HttpServerJob : public Job { - HttpServerJob(HttpServerJob const&) = delete; - HttpServerJob& operator=(HttpServerJob const&) = delete; +class GeneralServerJob : public Job { + GeneralServerJob(GeneralServerJob const&) = delete; + GeneralServerJob& operator=(GeneralServerJob const&) = delete; public: - HttpServerJob(GeneralServer*, arangodb::WorkItem::uptr&, - bool isAsync = false); + GeneralServerJob(GeneralServer*, arangodb::WorkItem::uptr, + bool isAsync = false); - ~HttpServerJob(); + ~GeneralServerJob(); public: RestHandler* handler() const { return _handler.get(); } diff --git a/arangod/GeneralServer/VppCommTask.cpp b/arangod/GeneralServer/VppCommTask.cpp index 7d8237a26f..d86ca8f087 100644 --- a/arangod/GeneralServer/VppCommTask.cpp +++ b/arangod/GeneralServer/VppCommTask.cpp @@ -26,12 +26,12 @@ #include "Basics/HybridLogicalClock.h" #include "Basics/StringBuffer.h" #include "Basics/VelocyPackHelper.h" -#include "Meta/conversion.h" #include "GeneralServer/GeneralServer.h" #include "GeneralServer/GeneralServerFeature.h" #include "GeneralServer/RestHandler.h" #include "GeneralServer/RestHandlerFactory.h" #include "Logger/LoggerFeature.h" +#include "Meta/conversion.h" #include "Scheduler/Scheduler.h" #include "Scheduler/SchedulerFeature.h" #include "VocBase/ticks.h" @@ -278,6 +278,7 @@ bool VppCommTask::processRead() { // CASE 1: message is in one chunk if (chunkHeader._isFirst && chunkHeader._chunk == 1) { + LOG_TOPIC(DEBUG, Logger::COMMUNICATION) << "chunk contains single message"; std::size_t payloads = 0; try { @@ -306,17 +307,19 @@ bool VppCommTask::processRead() { // } doExecute = true; - LOG_TOPIC(DEBUG, Logger::COMMUNICATION) << "CASE 1"; } // CASE 2: message is in multiple chunks auto incompleteMessageItr = _incompleteMessages.find(chunkHeader._messageID); // CASE 2a: chunk starts new message if (chunkHeader._isFirst) { // first chunk of multi chunk message + LOG_TOPIC(DEBUG, Logger::COMMUNICATION) << "chunk starts a new message"; if (incompleteMessageItr != _incompleteMessages.end()) { - throw std::logic_error( - "Message should be first but is already in the Map of incomplete " - "messages"); + LOG_TOPIC(DEBUG, Logger::COMMUNICATION) + << "Message should be first but is already in the Map of incomplete " + "messages"; + closeTask(rest::ResponseCode::BAD); + return false; } // TODO: is a 32bit value sufficient for the messageLength here? @@ -327,17 +330,19 @@ bool VppCommTask::processRead() { auto insertPair = _incompleteMessages.emplace( std::make_pair(chunkHeader._messageID, std::move(message))); if (!insertPair.second) { - throw std::logic_error("insert failed"); - closeTask(); + LOG_TOPIC(DEBUG, Logger::COMMUNICATION) << "insert failed"; + closeTask(rest::ResponseCode::BAD); + return false; } - LOG_TOPIC(DEBUG, Logger::COMMUNICATION) << "CASE 2a"; - // CASE 2b: chunk continues a message } else { // followup chunk of some mesage + LOG_TOPIC(DEBUG, Logger::COMMUNICATION) << "chunk continues a message"; if (incompleteMessageItr == _incompleteMessages.end()) { - throw std::logic_error("found message without previous part"); - closeTask(); + LOG_TOPIC(DEBUG, Logger::COMMUNICATION) + << "found message without previous part"; + closeTask(rest::ResponseCode::BAD); + return false; } auto& im = incompleteMessageItr->second; // incomplete Message im._currentChunk++; @@ -347,6 +352,7 @@ bool VppCommTask::processRead() { // MESSAGE COMPLETE if (im._currentChunk == im._numberOfChunks - 1 /* zero based counting */) { + LOG_TOPIC(DEBUG, Logger::COMMUNICATION) << "chunk completes a message"; std::size_t payloads = 0; try { @@ -374,9 +380,9 @@ bool VppCommTask::processRead() { // check length doExecute = true; - LOG_TOPIC(DEBUG, Logger::COMMUNICATION) << "CASE 2b - complete"; } - LOG_TOPIC(DEBUG, Logger::COMMUNICATION) << "CASE 2b - still incomplete"; + LOG_TOPIC(DEBUG, Logger::COMMUNICATION) + << "chunk does not complete a message"; } read_maybe_only_part_of_buffer = true; @@ -394,14 +400,17 @@ bool VppCommTask::processRead() { if (doExecute) { VPackSlice header = message.header(); - LOG_TOPIC(DEBUG, Logger::COMMUNICATION) - << "got request:" << header.toJson(); + LOG_TOPIC(DEBUG, Logger::COMMUNICATION) << "got request:" + << header.toJson(); int type = meta::underlyingValue(rest::RequestType::ILLEGAL); try { type = header.at(1).getInt(); } catch (std::exception const& e) { - throw std::runtime_error( - std::string("Error during Parsing of VppHeader: ") + e.what()); + handleSimpleError(rest::ResponseCode::BAD, chunkHeader._messageID); + LOG_TOPIC(DEBUG, Logger::COMMUNICATION) + << std::string("VPack Validation failed!") + e.what(); + closeTask(rest::ResponseCode::BAD); + return false; } if (type == 1000) { // do auth diff --git a/cmake/FindPythonInterp.cmake b/cmake/FindPythonInterp.cmake new file mode 100644 index 0000000000..e194185f22 --- /dev/null +++ b/cmake/FindPythonInterp.cmake @@ -0,0 +1,165 @@ +#.rst: +# FindPythonInterp +# ---------------- +# +# Find python interpreter +# +# This module finds if Python interpreter is installed and determines +# where the executables are. This code sets the following variables: +# +# :: +# +# PYTHONINTERP_FOUND - Was the Python executable found +# PYTHON_EXECUTABLE - path to the Python interpreter +# +# +# +# :: +# +# PYTHON_VERSION_STRING - Python version found e.g. 2.5.2 +# PYTHON_VERSION_MAJOR - Python major version found e.g. 2 +# PYTHON_VERSION_MINOR - Python minor version found e.g. 5 +# PYTHON_VERSION_PATCH - Python patch version found e.g. 2 +# +# +# +# The Python_ADDITIONAL_VERSIONS variable can be used to specify a list +# of version numbers that should be taken into account when searching +# for Python. You need to set this variable before calling +# find_package(PythonInterp). +# +# If calling both ``find_package(PythonInterp)`` and +# ``find_package(PythonLibs)``, call ``find_package(PythonInterp)`` first to +# get the currently active Python version by default with a consistent version +# of PYTHON_LIBRARIES. + +#============================================================================= +# Copyright 2005-2010 Kitware, Inc. +# Copyright 2011 Bjoern Ricks +# Copyright 2012 Rolf Eike Beer +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +unset(_Python_NAMES) + +set(_PYTHON1_VERSIONS 1.6 1.5) +set(_PYTHON2_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0) +set(_PYTHON3_VERSIONS 3.6 3.5 3.4 3.3 3.2 3.1 3.0) + +if(PythonInterp_FIND_VERSION) + if(PythonInterp_FIND_VERSION_COUNT GREATER 1) + set(_PYTHON_FIND_MAJ_MIN "${PythonInterp_FIND_VERSION_MAJOR}.${PythonInterp_FIND_VERSION_MINOR}") + list(APPEND _Python_NAMES + python${_PYTHON_FIND_MAJ_MIN} + python${PythonInterp_FIND_VERSION_MAJOR}) + unset(_PYTHON_FIND_OTHER_VERSIONS) + if(NOT PythonInterp_FIND_VERSION_EXACT) + foreach(_PYTHON_V ${_PYTHON${PythonInterp_FIND_VERSION_MAJOR}_VERSIONS}) + if(NOT _PYTHON_V VERSION_LESS _PYTHON_FIND_MAJ_MIN) + list(APPEND _PYTHON_FIND_OTHER_VERSIONS ${_PYTHON_V}) + endif() + endforeach() + endif() + unset(_PYTHON_FIND_MAJ_MIN) + else() + list(APPEND _Python_NAMES python${PythonInterp_FIND_VERSION_MAJOR}) + set(_PYTHON_FIND_OTHER_VERSIONS ${_PYTHON${PythonInterp_FIND_VERSION_MAJOR}_VERSIONS}) + endif() +else() + set(_PYTHON_FIND_OTHER_VERSIONS ${_PYTHON3_VERSIONS} ${_PYTHON2_VERSIONS} ${_PYTHON1_VERSIONS}) +endif() +find_program(PYTHON_EXECUTABLE NAMES ${_Python_NAMES}) + +# Set up the versions we know about, in the order we will search. Always add +# the user supplied additional versions to the front. +set(_Python_VERSIONS ${Python_ADDITIONAL_VERSIONS}) +# If FindPythonInterp has already found the major and minor version, +# insert that version next to get consistent versions of the interpreter and +# library. +if(DEFINED PYTHONLIBS_VERSION_STRING) + string(REPLACE "." ";" _PYTHONLIBS_VERSION "${PYTHONLIBS_VERSION_STRING}") + list(GET _PYTHONLIBS_VERSION 0 _PYTHONLIBS_VERSION_MAJOR) + list(GET _PYTHONLIBS_VERSION 1 _PYTHONLIBS_VERSION_MINOR) + list(APPEND _Python_VERSIONS ${_PYTHONLIBS_VERSION_MAJOR}.${_PYTHONLIBS_VERSION_MINOR}) +endif() +# Search for the current active python version first +list(APPEND _Python_VERSIONS ";") +list(APPEND _Python_VERSIONS ${_PYTHON_FIND_OTHER_VERSIONS}) + +unset(_PYTHON_FIND_OTHER_VERSIONS) +unset(_PYTHON1_VERSIONS) +unset(_PYTHON2_VERSIONS) +unset(_PYTHON3_VERSIONS) + +# Search for newest python version if python executable isn't found +if(NOT PYTHON_EXECUTABLE) + foreach(_CURRENT_VERSION IN LISTS _Python_VERSIONS) + set(_Python_NAMES python${_CURRENT_VERSION}) + if(WIN32) + list(APPEND _Python_NAMES python) + endif() + find_program(PYTHON_EXECUTABLE + NAMES ${_Python_NAMES} + PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}\\InstallPath] + ) + endforeach() +endif() + +# determine python version string +if(PYTHON_EXECUTABLE) + execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c + "import sys; sys.stdout.write(';'.join([str(x) for x in sys.version_info[:3]]))" + OUTPUT_VARIABLE _VERSION + RESULT_VARIABLE _PYTHON_VERSION_RESULT + ERROR_QUIET) + if(NOT _PYTHON_VERSION_RESULT) + string(REPLACE ";" "." PYTHON_VERSION_STRING "${_VERSION}") + list(GET _VERSION 0 PYTHON_VERSION_MAJOR) + list(GET _VERSION 1 PYTHON_VERSION_MINOR) + list(GET _VERSION 2 PYTHON_VERSION_PATCH) + if(PYTHON_VERSION_PATCH EQUAL 0) + # it's called "Python 2.7", not "2.7.0" + string(REGEX REPLACE "\\.0$" "" PYTHON_VERSION_STRING "${PYTHON_VERSION_STRING}") + endif() + else() + # sys.version predates sys.version_info, so use that + execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "import sys; sys.stdout.write(sys.version)" + OUTPUT_VARIABLE _VERSION + RESULT_VARIABLE _PYTHON_VERSION_RESULT + ERROR_QUIET) + if(NOT _PYTHON_VERSION_RESULT) + string(REGEX REPLACE " .*" "" PYTHON_VERSION_STRING "${_VERSION}") + string(REGEX REPLACE "^([0-9]+)\\.[0-9]+.*" "\\1" PYTHON_VERSION_MAJOR "${PYTHON_VERSION_STRING}") + string(REGEX REPLACE "^[0-9]+\\.([0-9])+.*" "\\1" PYTHON_VERSION_MINOR "${PYTHON_VERSION_STRING}") + if(PYTHON_VERSION_STRING MATCHES "^[0-9]+\\.[0-9]+\\.([0-9]+)") + set(PYTHON_VERSION_PATCH "${CMAKE_MATCH_1}") + else() + set(PYTHON_VERSION_PATCH "0") + endif() + else() + # sys.version was first documented for Python 1.5, so assume + # this is older. + set(PYTHON_VERSION_STRING "1.4") + set(PYTHON_VERSION_MAJOR "1") + set(PYTHON_VERSION_MINOR "4") + set(PYTHON_VERSION_PATCH "0") + endif() + endif() + unset(_PYTHON_VERSION_RESULT) + unset(_VERSION) +endif() + +# handle the QUIETLY and REQUIRED arguments and set PYTHONINTERP_FOUND to TRUE if +# all listed variables are TRUE +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PythonInterp REQUIRED_VARS PYTHON_EXECUTABLE VERSION_VAR PYTHON_VERSION_STRING) + +mark_as_advanced(PYTHON_EXECUTABLE)