diff options
author | Stefan Radomski <github@mintwerk.de> | 2014-03-07 13:04:23 (GMT) |
---|---|---|
committer | Stefan Radomski <github@mintwerk.de> | 2014-03-07 13:04:23 (GMT) |
commit | 325ecf075e1bfee5a830ad345c126802b8b55fcf (patch) | |
tree | c46ed5fcbf44ea1a32517f8ba3d6d9a066b6fed8 | |
parent | b5fd8687df8d5bf068f747070662a4bad4096f88 (diff) | |
parent | ca46aa711fb5d08a8fd1cc6b91593c281189e8e3 (diff) | |
download | uscxml-325ecf075e1bfee5a830ad345c126802b8b55fcf.zip uscxml-325ecf075e1bfee5a830ad345c126802b8b55fcf.tar.gz uscxml-325ecf075e1bfee5a830ad345c126802b8b55fcf.tar.bz2 |
Merge pull request #19 from sradomski/master
More work on uscxml debugger
26 files changed, 2635 insertions, 1162 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index facfcd3..f1ba3bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -854,6 +854,7 @@ endif() # Binaries and tests ############################################################ +list(SORT USCXML_FILES) # we cannot use source groups in sub directories! foreach( FILE ${USCXML_FILES} ) get_filename_component(PATH ${FILE} PATH) @@ -889,6 +890,15 @@ foreach( FILE ${USCXML_FILES} ) # STRING(REGEX MATCH "[^\\/]*$" COMP_NAME ${PATH}) # source_group("Interpreter\\URL" FILES ${FILE}) + elseif (${FILE} MATCHES ".*\\/debug\\/.*") + source_group("Interpreter\\debug" FILES ${FILE}) + elseif (${FILE} MATCHES ".*\\/util\\/.*") + source_group("Interpreter\\util" FILES ${FILE}) + elseif (${FILE} MATCHES ".*\\/concurrency\\/.*") + source_group("Interpreter\\concurrency" FILES ${FILE}) + elseif (${FILE} MATCHES ".*\\/server\\/.*") + source_group("Interpreter\\server" FILES ${FILE}) + else () source_group(Interpreter FILES ${FILE}) endif() diff --git a/apps/uscxml-debug.cpp b/apps/uscxml-debug.cpp index 5b9c989..e155ace 100644 --- a/apps/uscxml-debug.cpp +++ b/apps/uscxml-debug.cpp @@ -3,10 +3,14 @@ #include "uscxml/DOMUtils.h" #include "uscxml/UUID.h" #include "uscxml/debug/SCXMLDotWriter.h" +#include "uscxml/debug/Breakpoint.h" +#include "uscxml/debug/Debugger.h" +#include "uscxml/debug/DebuggerServlet.h" #include <glog/logging.h> #include <time.h> // mktime #include <boost/algorithm/string.hpp> +#include <map> #ifdef HAS_SIGNAL_H #include <signal.h> @@ -22,292 +26,52 @@ using namespace uscxml; -class Debugger : public HTTPServlet, public InterpreterMonitor, public google::LogSink { -public: - class BreakPoint { - public: - - enum When { - UNDEF_WHEN, AFTER, BEFORE, ON - }; - - enum Subject { - UNDEF_SUBJECT, STATE, TRANSITION, CONFIGURATION, EVENT - }; - - enum Action { - UNDEF_ACTION, ENTER, EXIT - }; - - BreakPoint(const Data& data) { - subject = UNDEF_SUBJECT; - when = UNDEF_WHEN; - action = UNDEF_ACTION; - - if (data.hasKey("action")) { - if (false) { - } else if (iequals(data["action"], "enter")) { - action = ENTER; - } else if (iequals(data["action"], "exit")) { - action = EXIT; - } - } - if (data.hasKey("time")) { - if (false) { - } else if (iequals(data["time"], "before")) { - when = BEFORE; - } else if (iequals(data["time"], "after")) { - when = AFTER; - } else if (iequals(data["time"], "on")) { - when = ON; - } - } - if (data.hasKey("subject")) { - if (false) { - } else if (iequals(data["subject"], "state")) { - subject = STATE; - if (data.hasKey("stateId")) - state = data["stateId"].atom; - } else if (iequals(data["subject"], "transition")) { - subject = TRANSITION; - if (data.hasKey("fromStateId")) - fromState = data["fromStateId"].atom; - if (data.hasKey("toStateId")) - fromState = data["toStateId"].atom; - } else if (iequals(data["subject"], "event")) { - subject = EVENT; - if (data.hasKey("eventName")) - eventName = data["eventName"].atom; - } else if (iequals(data["subject"], "configuration")) { - subject = CONFIGURATION; - } else if (iequals(data["subject"], "microstep")) { - subject = CONFIGURATION; - } - } - - if (data.hasKey("condition")) { - condition = data["condition"].atom; - } - } - - bool isValid() { - return true; - } - - protected: - When when; - Subject subject; - Action action; - - std::string state; - std::string toState; - std::string fromState; - std::string eventName; - - std::string condition; - - }; - - class LogMessage { - public: - google::LogSeverity severity; - std::string full_filename; - std::string base_filename; - int line; - const struct ::tm* tm_time; - std::string message; - std::string formatted; - - Data toData() { - Data data; - data.compound["severity"] = severity; - data.compound["fullFilename"] = Data(full_filename, Data::VERBATIM); - data.compound["baseFilename"] = Data(base_filename, Data::VERBATIM); - data.compound["line"] = line; - data.compound["message"] = Data(message, Data::VERBATIM); - data.compound["time"] = mktime((struct ::tm*)tm_time); - data.compound["formatted"] = Data(formatted, Data::VERBATIM); - return data; - } - }; - - std::string _url; - Interpreter _interpreter; - HTTPServer::Request _debugReq; - tthread::recursive_mutex _mutex; - std::list<LogMessage> _logMessages; - std::map<std::string, BreakPoint> _breakPoints; - - virtual ~Debugger() { - } - - // callbacks from http requests - - void debug(const HTTPServer::Request& request) { - tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); - - // save request and run until we reach a breakpoint - _debugReq = request; - _interpreter.interpret(); - } - - void connect(const HTTPServer::Request& request) { - Data replyData; - replyData.compound["status"] = Data("success", Data::VERBATIM); - returnData(request, replyData); - } - - void disconnect(const HTTPServer::Request& request) { - Data replyData; - replyData.compound["status"] = Data("success", Data::VERBATIM); - returnData(request, replyData); - } - - void prepare(const HTTPServer::Request& request) { - tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); - - _interpreter = Interpreter::fromXML(request.data["content"].atom); - - Data replyData; - if (_interpreter) { - // register ourself as a monitor - _interpreter.addMonitor(this); - replyData.compound["status"] = Data("success", Data::VERBATIM); - } else { - replyData.compound["status"] = Data("failure", Data::VERBATIM); - } - returnData(request, replyData); - } - - void addBreakPoint(const HTTPServer::Request& request) { - BreakPoint breakPoint(request.data["content"]); - Data replyData; - if (breakPoint.isValid()) { - replyData.compound["status"] = Data("success", Data::VERBATIM); - } else { - replyData.compound["status"] = Data("failure", Data::VERBATIM); - } - returnData(request, replyData); - } - - // helpers - - void returnData(const HTTPServer::Request& request, Data replyData) { - //always include latest log - while(_logMessages.size() > 0) { - replyData.compound["log"].array.push_back(_logMessages.front().toData()); - _logMessages.pop_front(); - } - - HTTPServer::Reply reply(request); - reply.headers["Content-type"] = "application/json"; - reply.headers["Access-Control-Allow-Origin"] = "*"; - reply.content = Data::toJSON(replyData); - HTTPServer::reply(reply); - } - - bool isCORS(const HTTPServer::Request& request) { - return (request.data["type"].atom == "options" && - request.data["header"].hasKey("Origin") && - request.data["header"].hasKey("Access-Control-Request-Method")); - } - - void handleCORS(const HTTPServer::Request& request) { - HTTPServer::Reply corsReply(request); - if (request.data["header"].hasKey("Origin")) { - corsReply.headers["Access-Control-Allow-Origin"] = request.data["header"]["Origin"].atom; - } else { - corsReply.headers["Access-Control-Allow-Origin"] = "*"; - } - if (request.data["header"].hasKey("Access-Control-Request-Method")) - corsReply.headers["Access-Control-Allow-Methods"] = request.data["header"]["Access-Control-Request-Method"].atom; - if (request.data["header"].hasKey("Access-Control-Request-Headers")) - corsReply.headers["Access-Control-Allow-Headers"] = request.data["header"]["Access-Control-Request-Headers"].atom; - -// std::cout << "CORS!" << std::endl << request << std::endl; - HTTPServer::reply(corsReply); - } - - // HTTPServlet - - bool httpRecvRequest(const HTTPServer::Request& request) { - if (isCORS(request)) { - handleCORS(request); - return true; - } - - std::cout << Data::toJSON(request.data) << std::endl; - - if (false) { - } else if (boost::istarts_with(request.data["path"].atom, "/connect")) { - connect(request); - } else if (boost::istarts_with(request.data["path"].atom, "/disconnect")) { - disconnect(request); - } else if (boost::istarts_with(request.data["path"].atom, "/prepare")) { - prepare(request); - } else if (boost::istarts_with(request.data["path"].atom, "/debug")) { - debug(request); - } else if (boost::istarts_with(request.data["path"].atom, "/breakpoint/add")) { - addBreakPoint(request); - } - return true; - } - void setURL(const std::string& url) { - _url = url; - } - - // InterpreterMonitor - void onStableConfiguration(Interpreter interpreter) { - } - - void beforeCompletion(Interpreter interpreter) {} - void afterCompletion(Interpreter interpreter) {} - void beforeMicroStep(Interpreter interpreter) {} - void beforeTakingTransitions(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& transitions) {} - void beforeEnteringStates(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& statesToEnter) {} - void afterEnteringStates(Interpreter interpreter) {} - void beforeExitingStates(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& statesToExit) {} - void afterExitingStates(Interpreter interpreter) {} - - // google::LogSink - - virtual void send(google::LogSeverity severity, const char* full_filename, - const char* base_filename, int line, - const struct ::tm* tm_time, - const char* message, size_t message_len) { - - tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); - - LogMessage msg; - msg.severity = severity; - msg.full_filename = full_filename; - msg.base_filename = base_filename; - msg.line = line; - msg.tm_time = tm_time; - msg.message = std::string(message, message_len); - msg.formatted = ToString(severity, base_filename, line, tm_time, message, message_len); - - _logMessages.push_back(msg); - } - -}; +//class Debugger : public HTTPServlet { +//public: +// +// std::string _url; +// Interpreter _interpreter; +// +// HTTPServer::Request _clientConn; // a request the client renews everytime +// concurrency::BlockingQueue<Data> _sendQueue; // queue of things we have to return to the client +// +// tthread::recursive_mutex _mutex; +// tthread::condition_variable _resumeCond; +// +// std::set<Breakpoint> _breakPoints; +// std::string _sessionId; +// +// DebuggerMonitor _monitor; +// +// virtual ~Debugger() { +// } +// +// // callbacks from http requests +// +// +// // helpers +// +// +// +// +//}; int main(int argc, char** argv) { using namespace uscxml; InterpreterOptions options = InterpreterOptions::fromCmdLine(argc, argv); - Debugger debugger; - + DebuggerServlet debuggerServlet; + // setup logging google::InitGoogleLogging(argv[0]); - google::AddLogSink(&debugger); + google::AddLogSink(&debuggerServlet); // setup HTTP server HTTPServer::getInstance(18088, 18089, NULL); - HTTPServer::getInstance()->registerServlet("/", &debugger); + HTTPServer::getInstance()->registerServlet("/", &debuggerServlet); while(true) tthread::this_thread::sleep_for(tthread::chrono::seconds(1)); diff --git a/contrib/build-scripts/build-uscxml-android.sh b/contrib/build-scripts/build-uscxml-android.sh new file mode 100755 index 0000000..9cda4af --- /dev/null +++ b/contrib/build-scripts/build-uscxml-android.sh @@ -0,0 +1,89 @@ +#!/bin/bash + +# +# build all of uscxml for android +# + +# exit on error +set -e + +ME=`basename $0` +DIR="$( cd "$( dirname "$0" )" && pwd )" +CWD=`pwd` +BUILD_DIR="/tmp/build-uscxml-android" + +rm -rf ${BUILD_DIR} && mkdir -p ${BUILD_DIR} &> /dev/null +cd ${BUILD_DIR} + + +if [ ! -d "${ANDROID_NDK}" ]; then + echo + echo No Android NDK at ${ANDROID_NDK} + echo export ANDROID_NDK as the NDK root + echo + exit +fi +echo +echo Using Android NDK at ${ANDROID_NDK} +echo + +#. ${DIR}/find-android-ndk.sh + +# ANDROID_ABI=armeabi-v7a - specifies the target Application Binary +# Interface (ABI). This option nearly matches to the APP_ABI variable +# used by ndk-build tool from Android NDK. +# +# Possible targets are: +# "armeabi" - matches to the NDK ABI with the same name. +# See ${ANDROID_NDK}/docs/CPU-ARCH-ABIS.html for the documentation. +# "armeabi-v7a" - matches to the NDK ABI with the same name. +# See ${ANDROID_NDK}/docs/CPU-ARCH-ABIS.html for the documentation. +# "armeabi-v7a with NEON" - same as armeabi-v7a, but +# sets NEON as floating-point unit +# "armeabi-v7a with VFPV3" - same as armeabi-v7a, but +# sets VFPV3 as floating-point unit (has 32 registers instead of 16). +# "armeabi-v6 with VFP" - tuned for ARMv6 processors having VFP. +# "x86" - matches to the NDK ABI with the same name. +# See ${ANDROID_NDK}/docs/CPU-ARCH-ABIS.html for the documentation. +# "mips" - matches to the NDK ABI with the same name +# (not testes on real devices) + +mkdir -p ${BUILD_DIR} &> /dev/null +cd ${BUILD_DIR} + +rm -rf * +cmake ${DIR}/../../ \ +-DCMAKE_TOOLCHAIN_FILE=${DIR}/../cmake/CrossCompile-Android.cmake \ +-DBUILD_SHARED_LIBS=OFF \ +-DANDROID_ABI="armeabi" \ +-DCMAKE_BUILD_TYPE=Debug +make -j2 +make -j2 java + +rm -rf * +cmake ${DIR}/../../ \ +-DCMAKE_TOOLCHAIN_FILE=${DIR}/../cmake/CrossCompile-Android.cmake \ +-DBUILD_SHARED_LIBS=OFF \ +-DANDROID_ABI="armeabi" \ +-DCMAKE_BUILD_TYPE=Release +make -j2 +make -j2 java + +rm -rf * +cmake ${DIR}/../../ \ +-DCMAKE_TOOLCHAIN_FILE=${DIR}/../cmake/CrossCompile-Android.cmake \ +-DBUILD_SHARED_LIBS=OFF \ +-DANDROID_ABI="x86" \ +-DCMAKE_BUILD_TYPE=Debug +make -j2 +make -j2 java + +rm -rf * +cmake ${DIR}/../../ \ +-DCMAKE_TOOLCHAIN_FILE=${DIR}/../cmake/CrossCompile-Android.cmake \ +-DBUILD_SHARED_LIBS=OFF \ +-DANDROID_ABI="x86" \ +-DCMAKE_BUILD_TYPE=Release +make -j2 +make -j2 java + diff --git a/contrib/cmake/CrossCompile-Android.cmake b/contrib/cmake/CrossCompile-Android.cmake index 37c9ad1..fabc2a3 100644 --- a/contrib/cmake/CrossCompile-Android.cmake +++ b/contrib/cmake/CrossCompile-Android.cmake @@ -1,9 +1,39 @@ +# Copyright (c) 2010-2011, Ethan Rublee +# Copyright (c) 2011-2014, Andrey Kamaev +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + # ------------------------------------------------------------------------------ -# Android CMake toolchain file, for use with the Android NDK r5-r8 +# Android CMake toolchain file, for use with the Android NDK r5-r9 # Requires cmake 2.6.3 or newer (2.8.5 or newer is recommended). -# See home page: http://code.google.com/p/android-cmake/ +# See home page: https://github.com/taka-no-me/android-cmake # -# The file is mantained by the OpenCV project. And also can be found at +# The file is mantained by the OpenCV project. The latest version can be get at # http://code.opencv.org/projects/opencv/repository/revisions/master/changes/android/android.toolchain.cmake # # Usage Linux: @@ -29,7 +59,7 @@ # $ cmake.exe -G"MinGW Makefiles" # -DCMAKE_TOOLCHAIN_FILE=path\to\the\android.toolchain.cmake # -DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%\prebuilt\windows\bin\make.exe" .. -# $ "%ANDROID_NDK%\prebuilt\windows\bin\make.exe" +# $ cmake.exe --build . # # # Options (can be set as cmake parameters: -D<option_name>=<value>): @@ -41,7 +71,7 @@ # (ignored if ANDROID_NDK is set). # Can be set as environment variable. Can be set only at first cmake run. # -# ANDROID_ABI=armeabi-v7a - specifies the target Application Binary +# ANDROID_ABI=armeabi-v7a - specifies the target Application Binary # Interface (ABI). This option nearly matches to the APP_ABI variable # used by ndk-build tool from Android NDK. # @@ -57,30 +87,80 @@ # "armeabi-v6 with VFP" - tuned for ARMv6 processors having VFP. # "x86" - matches to the NDK ABI with the same name. # See ${ANDROID_NDK}/docs/CPU-ARCH-ABIS.html for the documentation. -# "mips" - matches to the NDK ABI with the same name -# (not testes on real devices) +# "mips" - matches to the NDK ABI with the same name. +# See ${ANDROID_NDK}/docs/CPU-ARCH-ABIS.html for the documentation. # # ANDROID_NATIVE_API_LEVEL=android-8 - level of Android API compile for. -# Option is read-only when standalone toolchain used. +# Option is read-only when standalone toolchain is used. +# +# ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.6 - the name of compiler +# toolchain to be used. The list of possible values depends on the NDK +# version. For NDK r8c the possible values are: # -# ANDROID_FORCE_ARM_BUILD=OFF - set true to generate 32-bit ARM instructions -# instead of Thumb-1. Is not available for "x86" (inapplicable) and -# "armeabi-v6 with VFP" (forced) ABIs. +# * arm-linux-androideabi-4.4.3 +# * arm-linux-androideabi-4.6 +# * arm-linux-androideabi-clang3.1 +# * mipsel-linux-android-4.4.3 +# * mipsel-linux-android-4.6 +# * mipsel-linux-android-clang3.1 +# * x86-4.4.3 +# * x86-4.6 +# * x86-clang3.1 # -# ANDROID_NO_UNDEFINED=ON - set true to show all undefined symbols as linker +# ANDROID_FORCE_ARM_BUILD=OFF - set ON to generate 32-bit ARM instructions +# instead of Thumb. Is not available for "x86" (inapplicable) and +# "armeabi-v6 with VFP" (is forced to be ON) ABIs. +# +# ANDROID_NO_UNDEFINED=ON - set ON to show all undefined symbols as linker # errors even if they are not used. # -# ANDROID_SO_UNDEFINED=OFF - set true to allow undefined symbols in shared -# libraries. Automatically turned on for NDK r5x and r6x due to GLESv2 +# ANDROID_SO_UNDEFINED=OFF - set ON to allow undefined symbols in shared +# libraries. Automatically turned for NDK r5x and r6x due to GLESv2 # problems. # # LIBRARY_OUTPUT_PATH_ROOT=${CMAKE_SOURCE_DIR} - where to output binary # files. See additional details below. # -# ANDROID_SET_OBSOLETE_VARIABLES=ON - it set, then toolchain defines some -# obsolete variables which were set by previous versions of this file for +# ANDROID_SET_OBSOLETE_VARIABLES=ON - if set, then toolchain defines some +# obsolete variables which were used by previous versions of this file for # backward compatibility. # +# ANDROID_STL=gnustl_static - specify the runtime to use. +# +# Possible values are: +# none -> Do not configure the runtime. +# system -> Use the default minimal system C++ runtime library. +# Implies -fno-rtti -fno-exceptions. +# Is not available for standalone toolchain. +# system_re -> Use the default minimal system C++ runtime library. +# Implies -frtti -fexceptions. +# Is not available for standalone toolchain. +# gabi++_static -> Use the GAbi++ runtime as a static library. +# Implies -frtti -fno-exceptions. +# Available for NDK r7 and newer. +# Is not available for standalone toolchain. +# gabi++_shared -> Use the GAbi++ runtime as a shared library. +# Implies -frtti -fno-exceptions. +# Available for NDK r7 and newer. +# Is not available for standalone toolchain. +# stlport_static -> Use the STLport runtime as a static library. +# Implies -fno-rtti -fno-exceptions for NDK before r7. +# Implies -frtti -fno-exceptions for NDK r7 and newer. +# Is not available for standalone toolchain. +# stlport_shared -> Use the STLport runtime as a shared library. +# Implies -fno-rtti -fno-exceptions for NDK before r7. +# Implies -frtti -fno-exceptions for NDK r7 and newer. +# Is not available for standalone toolchain. +# gnustl_static -> Use the GNU STL as a static library. +# Implies -frtti -fexceptions. +# gnustl_shared -> Use the GNU STL as a shared library. +# Implies -frtti -fno-exceptions. +# Available for NDK r7b and newer. +# Silently degrades to gnustl_static if not available. +# +# ANDROID_STL_FORCE_FEATURES=ON - turn rtti and exceptions support based on +# chosen runtime. If disabled, then the user is responsible for settings +# these options. # # What?: # android-cmake toolchain searches for NDK/toolchain in the following order: @@ -94,15 +174,15 @@ # Make sure to do the following in your scripts: # SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${my_cxx_flags}" ) # SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${my_cxx_flags}" ) -# The flags will be prepopulated with critical flags, so don't loose them. -# Also be aware that toolchain also sets configuration-specific compiler -# flags and linker flags. +# The flags will be prepopulated with critical flags, so don't loose them. +# Also be aware that toolchain also sets configuration-specific compiler +# flags and linker flags. # # ANDROID and BUILD_ANDROID will be set to true, you may test any of these # variables to make necessary Android-specific configuration changes. # -# Also ARMEABI or ARMEABI_V7A or X86 will be set true, mutually exclusive. -# NEON option will be set true if VFP is set to NEON. +# Also ARMEABI or ARMEABI_V7A or X86 or MIPS will be set true, mutually +# exclusive. NEON option will be set true if VFP is set to NEON. # # LIBRARY_OUTPUT_PATH_ROOT should be set in cache to determine where Android # libraries will be installed. @@ -110,13 +190,9 @@ # under the ${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME} # (depending on the target ABI). This is convenient for Android packaging. # -# Authors: -# Ethan Rublee ethan.ruble@gmail.com -# Andrey Kamaev andrey.kamaev@itseez.com -# # Change Log: # - initial version December 2010 -# - modified April 2011 +# - April 2011 # [+] added possibility to build with NDK (without standalone toolchain) # [+] support cross-compilation on Windows (native, no cygwin support) # [+] added compiler option to force "char" type to be signed @@ -127,13 +203,13 @@ # [+] EXECUTABLE_OUTPUT_PATH is set by toolchain (required on Windows) # [~] Fixed bug with ANDROID_API_LEVEL variable # [~] turn off SWIG search if it is not found first time -# - modified May 2011 +# - May 2011 # [~] ANDROID_LEVEL is renamed to ANDROID_API_LEVEL # [+] ANDROID_API_LEVEL is detected by toolchain if not specified # [~] added guard to prevent changing of output directories on the first # cmake pass # [~] toolchain exits with error if ARM_TARGET is not recognized -# - modified June 2011 +# - June 2011 # [~] default NDK path is updated for version r5c # [+] variable CMAKE_SYSTEM_PROCESSOR is set based on ARM_TARGET # [~] toolchain install directory is added to linker paths @@ -141,13 +217,13 @@ # [+] added macro find_host_package, find_host_program to search # packages/programs on the host system # [~] fixed path to STL library -# - modified July 2011 +# - July 2011 # [~] fixed options caching # [~] search for all supported NDK versions # [~] allowed spaces in NDK path -# - modified September 2011 +# - September 2011 # [~] updated for NDK r6b -# - modified November 2011 +# - November 2011 # [*] rewritten for NDK r7 # [+] x86 toolchain support (experimental) # [+] added "armeabi-v6 with VFP" ABI for ARMv6 processors. @@ -160,33 +236,71 @@ # [~] ARM_TARGET is renamed to ANDROID_ABI # [~] ARMEABI_NDK_NAME is renamed to ANDROID_NDK_ABI_NAME # [~] ANDROID_API_LEVEL is renamed to ANDROID_NATIVE_API_LEVEL -# - modified January 2012 +# - January 2012 # [+] added stlport_static support (experimental) # [+] added special check for cygwin # [+] filtered out hidden files (starting with .) while globbing inside NDK # [+] automatically applied GLESv2 linkage fix for NDK revisions 5-6 # [+] added ANDROID_GET_ABI_RAWNAME to get NDK ABI names by CMake flags -# - modified February 2012 +# - February 2012 # [+] updated for NDK r7b # [~] fixed cmake try_compile() command # [~] Fix for missing install_name_tool on OS X -# - modified March 2012 +# - March 2012 # [~] fixed incorrect C compiler flags # [~] fixed CMAKE_SYSTEM_PROCESSOR change on ANDROID_ABI change # [+] improved toolchain loading speed # [+] added assembler language support (.S) # [+] allowed preset search paths and extra search suffixes -# - modified April 2012 +# - April 2012 # [+] updated for NDK r7c # [~] fixed most of problems with compiler/linker flags and caching # [+] added option ANDROID_FUNCTION_LEVEL_LINKING -# - modified May 2012 +# - May 2012 # [+] updated for NDK r8 # [+] added mips architecture support -# - modified August 2012 +# - August 2012 # [+] updated for NDK r8b -# [~] all intermediate files generated by toolchain are moved into CMakeFiles +# [~] all intermediate files generated by toolchain are moved to CMakeFiles # [~] libstdc++ and libsupc are removed from explicit link libraries +# [+] added CCache support (via NDK_CCACHE environment or cmake variable) +# [+] added gold linker support for NDK r8b +# [~] fixed mips linker flags for NDK r8b +# - September 2012 +# [+] added NDK release name detection (see ANDROID_NDK_RELEASE) +# [+] added support for all C++ runtimes from NDK +# (system, gabi++, stlport, gnustl) +# [+] improved warnings on known issues of NDKs +# [~] use gold linker as default if available (NDK r8b) +# [~] globally turned off rpath +# [~] compiler options are aligned with NDK r8b +# - October 2012 +# [~] fixed C++ linking: explicitly link with math library (OpenCV #2426) +# - November 2012 +# [+] updated for NDK r8c +# [+] added support for clang compiler +# - December 2012 +# [+] suppress warning about unused CMAKE_TOOLCHAIN_FILE variable +# [+] adjust API level to closest compatible as NDK does +# [~] fixed ccache full path search +# [+] updated for NDK r8d +# [~] compiler options are aligned with NDK r8d +# - March 2013 +# [+] updated for NDK r8e (x86 version) +# [+] support x86_64 version of NDK +# - April 2013 +# [+] support non-release NDK layouts (from Linaro git and Android git) +# [~] automatically detect if explicit link to crtbegin_*.o is needed +# - June 2013 +# [~] fixed stl include path for standalone toolchain made by NDK >= r8c +# - July 2013 +# [+] updated for NDK r9 +# - November 2013 +# [+] updated for NDK r9b +# - December 2013 +# [+] updated for NDK r9c +# - January 2014 +# [~] fix copying of shared STL # ------------------------------------------------------------------------------ cmake_minimum_required( VERSION 2.6.3 ) @@ -196,7 +310,11 @@ if( DEFINED CMAKE_CROSSCOMPILING ) return() endif() -get_property(_CMAKE_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE) +if( CMAKE_TOOLCHAIN_FILE ) + # touch toolchain variable only to suppress "unused variable" warning +endif() + +get_property( _CMAKE_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE ) if( _CMAKE_IN_TRY_COMPILE ) include( "${CMAKE_CURRENT_SOURCE_DIR}/../android.toolchain.config.cmake" OPTIONAL ) endif() @@ -206,7 +324,10 @@ set( CMAKE_SYSTEM_NAME Linux ) # this one not so much set( CMAKE_SYSTEM_VERSION 1 ) -set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" ) +# rpath makes low sence for Android +set( CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries." ) + +set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r9c -r9b -r9 -r8e -r8d -r8c -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" ) if(NOT DEFINED ANDROID_NDK_SEARCH_PATHS) if( CMAKE_HOST_WIN32 ) file( TO_CMAKE_PATH "$ENV{PROGRAMFILES}" ANDROID_NDK_SEARCH_PATHS ) @@ -276,15 +397,15 @@ macro( __INIT_VARIABLE var_name ) if( NOT "${__value}" STREQUAL "" ) if( __test_path ) if( EXISTS "${__value}" ) - set( ${var_name} "${__value}" ) - if( __obsolete ) + file( TO_CMAKE_PATH "${__value}" ${var_name} ) + if( __obsolete AND NOT _CMAKE_IN_TRY_COMPILE ) message( WARNING "Using value of obsolete variable ${__var} as initial value for ${var_name}. Please note, that ${__var} can be completely removed in future versions of the toolchain." ) endif() break() endif() else() set( ${var_name} "${__value}" ) - if( __obsolete ) + if( __obsolete AND NOT _CMAKE_IN_TRY_COMPILE ) message( WARNING "Using value of obsolete variable ${__var} as initial value for ${var_name}. Please note, that ${__var} can be completely removed in future versions of the toolchain." ) endif() break() @@ -295,6 +416,8 @@ macro( __INIT_VARIABLE var_name ) unset( __value ) unset( __values ) unset( __obsolete ) + elseif( __test_path ) + file( TO_CMAKE_PATH "${${var_name}}" ${var_name} ) endif() unset( __test_path ) endmacro() @@ -311,34 +434,26 @@ macro( __DETECT_NATIVE_API_LEVEL _var _path ) endmacro() macro( __DETECT_TOOLCHAIN_MACHINE_NAME _var _root ) - file( GLOB __gccExePath "${_root}/bin/*-gcc${TOOL_OS_SUFFIX}" ) - __LIST_FILTER( __gccExePath "bin/[.].*-gcc${TOOL_OS_SUFFIX}$" ) - list( LENGTH __gccExePath __gccExePathsCount ) - if( NOT __gccExePathsCount EQUAL 1 ) - message( WARNING "Could not uniquely determine machine name for compiler from ${_root}." ) - set( ${_var} "" ) + if( EXISTS "${_root}" ) + file( GLOB __gccExePath RELATIVE "${_root}/bin/" "${_root}/bin/*-gcc${TOOL_OS_SUFFIX}" ) + __LIST_FILTER( __gccExePath "^[.].*" ) + list( LENGTH __gccExePath __gccExePathsCount ) + if( NOT __gccExePathsCount EQUAL 1 AND NOT _CMAKE_IN_TRY_COMPILE ) + message( WARNING "Could not determine machine name for compiler from ${_root}" ) + set( ${_var} "" ) + else() + get_filename_component( __gccExeName "${__gccExePath}" NAME_WE ) + string( REPLACE "-gcc" "" ${_var} "${__gccExeName}" ) + endif() + unset( __gccExePath ) + unset( __gccExePathsCount ) + unset( __gccExeName ) else() - get_filename_component( __gccExeName "${__gccExePath}" NAME_WE ) - string( REPLACE "-gcc" "" ${_var} "${__gccExeName}" ) - endif() - unset( __gccExePath ) - unset( __gccExePathsCount ) - unset( __gccExeName ) -endmacro() - -macro( __COPY_IF_DIFFERENT _source _destination ) - execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${_source}" "${_destination}" RESULT_VARIABLE __fileCopyProcess ) - if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${_destination}") - message( SEND_ERROR "Failed copying of ${_source} to the ${_destination}" ) + set( ${_var} "" ) endif() - unset( __fileCopyProcess ) endmacro() -# stl version: by default gnustl_static will be used -set( ANDROID_USE_STLPORT FALSE CACHE BOOL "Experimental: use stlport_static instead of gnustl_static") -mark_as_advanced( ANDROID_USE_STLPORT ) - # fight against cygwin set( ANDROID_FORBID_SYGWIN TRUE CACHE BOOL "Prevent cmake from working under cygwin and using cygwin tools") mark_as_advanced( ANDROID_FORBID_SYGWIN ) @@ -356,19 +471,32 @@ if( ANDROID_FORBID_SYGWIN ) endif() endif() + # detect current host platform +if( NOT DEFINED ANDROID_NDK_HOST_X64 AND (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64" OR CMAKE_HOST_APPLE) ) + set( ANDROID_NDK_HOST_X64 1 CACHE BOOL "Try to use 64-bit compiler toolchain" ) + mark_as_advanced( ANDROID_NDK_HOST_X64 ) +endif() + set( TOOL_OS_SUFFIX "" ) if( CMAKE_HOST_APPLE ) - set( ANDROID_NDK_HOST_SYSTEM_NAME "darwin-x86" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME "darwin-x86_64" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME2 "darwin-x86" ) elseif( CMAKE_HOST_WIN32 ) - set( ANDROID_NDK_HOST_SYSTEM_NAME "windows" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME "windows-x86_64" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME2 "windows" ) set( TOOL_OS_SUFFIX ".exe" ) elseif( CMAKE_HOST_UNIX ) - set( ANDROID_NDK_HOST_SYSTEM_NAME "linux-x86" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME "linux-x86_64" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME2 "linux-x86" ) else() message( FATAL_ERROR "Cross-compilation on your platform is not supported by this cmake toolchain" ) endif() +if( NOT ANDROID_NDK_HOST_X64 ) + set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) +endif() + # see if we have path to Android NDK __INIT_VARIABLE( ANDROID_NDK PATH ENV_ANDROID_NDK ) if( NOT ANDROID_NDK ) @@ -404,18 +532,15 @@ endif( NOT ANDROID_NDK ) # remember found paths if( ANDROID_NDK ) get_filename_component( ANDROID_NDK "${ANDROID_NDK}" ABSOLUTE ) - # try to detect change - if( CMAKE_AR ) - string( LENGTH "${ANDROID_NDK}" __length ) - string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidNdkPreviousPath ) - if( NOT __androidNdkPreviousPath STREQUAL ANDROID_NDK ) - message( FATAL_ERROR "It is not possible to change path to the NDK on subsequent run." ) - endif() - unset( __androidNdkPreviousPath ) - unset( __length ) - endif() - set( ANDROID_NDK "${ANDROID_NDK}" CACHE INTERNAL "Path of the Android NDK" ) + set( ANDROID_NDK "${ANDROID_NDK}" CACHE INTERNAL "Path of the Android NDK" FORCE ) set( BUILD_WITH_ANDROID_NDK True ) + if( EXISTS "${ANDROID_NDK}/RELEASE.TXT" ) + file( STRINGS "${ANDROID_NDK}/RELEASE.TXT" ANDROID_NDK_RELEASE_FULL LIMIT_COUNT 1 REGEX r[0-9]+[a-z]? ) + string( REGEX MATCH r[0-9]+[a-z]? ANDROID_NDK_RELEASE "${ANDROID_NDK_RELEASE_FULL}" ) + else() + set( ANDROID_NDK_RELEASE "r1x" ) + set( ANDROID_NDK_RELEASE_FULL "unreleased" ) + endif() elseif( ANDROID_STANDALONE_TOOLCHAIN ) get_filename_component( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" ABSOLUTE ) # try to detect change @@ -428,11 +553,11 @@ elseif( ANDROID_STANDALONE_TOOLCHAIN ) unset( __androidStandaloneToolchainPreviousPath ) unset( __length ) endif() - set( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" CACHE INTERNAL "Path of the Android standalone toolchain" ) + set( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" CACHE INTERNAL "Path of the Android standalone toolchain" FORCE ) set( BUILD_WITH_STANDALONE_TOOLCHAIN True ) else() list(GET ANDROID_NDK_SEARCH_PATHS 0 ANDROID_NDK_SEARCH_PATH) - message( FATAL_ERROR "Could not find neither Android NDK nor Android standalone toolcahin. + message( FATAL_ERROR "Could not find neither Android NDK nor Android standalone toolchain. You should either set an environment variable: export ANDROID_NDK=~/my-android-ndk or @@ -442,6 +567,51 @@ else() sudo ln -s ~/my-android-toolchain ${ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH}" ) endif() +# android NDK layout +if( BUILD_WITH_ANDROID_NDK ) + if( NOT DEFINED ANDROID_NDK_LAYOUT ) + # try to automatically detect the layout + if( EXISTS "${ANDROID_NDK}/RELEASE.TXT") + set( ANDROID_NDK_LAYOUT "RELEASE" ) + elseif( EXISTS "${ANDROID_NDK}/../../linux-x86/toolchain/" ) + set( ANDROID_NDK_LAYOUT "LINARO" ) + elseif( EXISTS "${ANDROID_NDK}/../../gcc/" ) + set( ANDROID_NDK_LAYOUT "ANDROID" ) + endif() + endif() + set( ANDROID_NDK_LAYOUT "${ANDROID_NDK_LAYOUT}" CACHE STRING "The inner layout of NDK" ) + mark_as_advanced( ANDROID_NDK_LAYOUT ) + if( ANDROID_NDK_LAYOUT STREQUAL "LINARO" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment + set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../${ANDROID_NDK_HOST_SYSTEM_NAME}/toolchain" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH "" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" ) + elseif( ANDROID_NDK_LAYOUT STREQUAL "ANDROID" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment + set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../gcc/${ANDROID_NDK_HOST_SYSTEM_NAME}/arm" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH "" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" ) + else() # ANDROID_NDK_LAYOUT STREQUAL "RELEASE" + set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/toolchains" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME}" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME2}" ) + endif() + get_filename_component( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK_TOOLCHAINS_PATH}" ABSOLUTE ) + + # try to detect change of NDK + if( CMAKE_AR ) + string( LENGTH "${ANDROID_NDK_TOOLCHAINS_PATH}" __length ) + string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidNdkPreviousPath ) + if( NOT __androidNdkPreviousPath STREQUAL ANDROID_NDK_TOOLCHAINS_PATH ) + message( FATAL_ERROR "It is not possible to change the path to the NDK on subsequent CMake run. You must remove all generated files from your build folder first. + " ) + endif() + unset( __androidNdkPreviousPath ) + unset( __length ) + endif() +endif() + + # get all the details about standalone toolchain if( BUILD_WITH_STANDALONE_TOOLCHAIN ) __DETECT_NATIVE_API_LEVEL( ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot/usr/include/android/api-level.h" ) @@ -458,55 +628,94 @@ if( BUILD_WITH_STANDALONE_TOOLCHAIN ) elseif( __availableToolchainMachines MATCHES mipsel ) set( __availableToolchainArchs "mipsel" ) endif() - if( ANDROID_COMPILER_VERSION ) - # do not run gcc every time because it is relatevely expencive - set( __availableToolchainCompilerVersions "${ANDROID_COMPILER_VERSION}" ) - else() - execute_process( COMMAND "${ANDROID_STANDALONE_TOOLCHAIN}/bin/${__availableToolchainMachines}-gcc${TOOL_OS_SUFFIX}" --version - OUTPUT_VARIABLE __availableToolchainCompilerVersions OUTPUT_STRIP_TRAILING_WHITESPACE ) - string( REGEX MATCH "[0-9]+.[0-9]+.[0-9]+" __availableToolchainCompilerVersions "${__availableToolchainCompilerVersions}" ) + execute_process( COMMAND "${ANDROID_STANDALONE_TOOLCHAIN}/bin/${__availableToolchainMachines}-gcc${TOOL_OS_SUFFIX}" -dumpversion + OUTPUT_VARIABLE __availableToolchainCompilerVersions OUTPUT_STRIP_TRAILING_WHITESPACE ) + string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9]+)?" __availableToolchainCompilerVersions "${__availableToolchainCompilerVersions}" ) + if( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/bin/clang${TOOL_OS_SUFFIX}" ) + list( APPEND __availableToolchains "standalone-clang" ) + list( APPEND __availableToolchainMachines ${__availableToolchainMachines} ) + list( APPEND __availableToolchainArchs ${__availableToolchainArchs} ) + list( APPEND __availableToolchainCompilerVersions ${__availableToolchainCompilerVersions} ) endif() endif() +macro( __GLOB_NDK_TOOLCHAINS __availableToolchainsVar __availableToolchainsLst __toolchain_subpath ) + foreach( __toolchain ${${__availableToolchainsLst}} ) + if( "${__toolchain}" MATCHES "-clang3[.][0-9]$" AND NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}${__toolchain_subpath}" ) + string( REGEX REPLACE "-clang3[.][0-9]$" "-4.6" __gcc_toolchain "${__toolchain}" ) + else() + set( __gcc_toolchain "${__toolchain}" ) + endif() + __DETECT_TOOLCHAIN_MACHINE_NAME( __machine "${ANDROID_NDK_TOOLCHAINS_PATH}/${__gcc_toolchain}${__toolchain_subpath}" ) + if( __machine ) + string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9x]+)?$" __version "${__gcc_toolchain}" ) + if( __machine MATCHES i686 ) + set( __arch "x86" ) + elseif( __machine MATCHES arm ) + set( __arch "arm" ) + elseif( __machine MATCHES mipsel ) + set( __arch "mipsel" ) + endif() + list( APPEND __availableToolchainMachines "${__machine}" ) + list( APPEND __availableToolchainArchs "${__arch}" ) + list( APPEND __availableToolchainCompilerVersions "${__version}" ) + list( APPEND ${__availableToolchainsVar} "${__toolchain}" ) + endif() + unset( __gcc_toolchain ) + endforeach() +endmacro() + # get all the details about NDK if( BUILD_WITH_ANDROID_NDK ) file( GLOB ANDROID_SUPPORTED_NATIVE_API_LEVELS RELATIVE "${ANDROID_NDK}/platforms" "${ANDROID_NDK}/platforms/android-*" ) string( REPLACE "android-" "" ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_SUPPORTED_NATIVE_API_LEVELS}" ) - file( GLOB __availableToolchains RELATIVE "${ANDROID_NDK}/toolchains" "${ANDROID_NDK}/toolchains/*" ) - __LIST_FILTER( __availableToolchains "^[.]" ) + set( __availableToolchains "" ) set( __availableToolchainMachines "" ) set( __availableToolchainArchs "" ) set( __availableToolchainCompilerVersions "" ) - foreach( __toolchain ${__availableToolchains} ) - __DETECT_TOOLCHAIN_MACHINE_NAME( __machine "${ANDROID_NDK}/toolchains/${__toolchain}/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME}" ) - if( __machine ) - string( REGEX MATCH "[0-9]+[.][0-9]+[.]*[0-9]*$" __version "${__toolchain}" ) - string( REGEX MATCH "^[^-]+" __arch "${__toolchain}" ) - list( APPEND __availableToolchainMachines "${__machine}" ) - list( APPEND __availableToolchainArchs "${__arch}" ) - list( APPEND __availableToolchainCompilerVersions "${__version}" ) - else() - list( REMOVE_ITEM __availableToolchains "${__toolchain}" ) + if( ANDROID_TOOLCHAIN_NAME AND EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_TOOLCHAIN_NAME}/" ) + # do not go through all toolchains if we know the name + set( __availableToolchainsLst "${ANDROID_TOOLCHAIN_NAME}" ) + __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) + if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 ) + __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" ) + if( __availableToolchains ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} ) + endif() endif() - endforeach() + endif() + if( NOT __availableToolchains ) + file( GLOB __availableToolchainsLst RELATIVE "${ANDROID_NDK_TOOLCHAINS_PATH}" "${ANDROID_NDK_TOOLCHAINS_PATH}/*" ) + if( __availableToolchains ) + list(SORT __availableToolchainsLst) # we need clang to go after gcc + endif() + __LIST_FILTER( __availableToolchainsLst "^[.]" ) + __LIST_FILTER( __availableToolchainsLst "llvm" ) + __LIST_FILTER( __availableToolchainsLst "renderscript" ) + __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) + if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 ) + __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" ) + if( __availableToolchains ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} ) + endif() + endif() + endif() if( NOT __availableToolchains ) - message( FATAL_ERROR "Could not any working toolchain in the NDK. Probably your Android NDK is broken." ) + message( FATAL_ERROR "Could not find any working toolchain in the NDK. Probably your Android NDK is broken." ) endif() endif() # build list of available ABIs +set( ANDROID_SUPPORTED_ABIS "" ) +set( __uniqToolchainArchNames ${__availableToolchainArchs} ) +list( REMOVE_DUPLICATES __uniqToolchainArchNames ) +list( SORT __uniqToolchainArchNames ) +foreach( __arch ${__uniqToolchainArchNames} ) + list( APPEND ANDROID_SUPPORTED_ABIS ${ANDROID_SUPPORTED_ABIS_${__arch}} ) +endforeach() +unset( __uniqToolchainArchNames ) if( NOT ANDROID_SUPPORTED_ABIS ) - set( ANDROID_SUPPORTED_ABIS "" ) - set( __uniqToolchainArchNames ${__availableToolchainArchs} ) - list( REMOVE_DUPLICATES __uniqToolchainArchNames ) - list( SORT __uniqToolchainArchNames ) - foreach( __arch ${__uniqToolchainArchNames} ) - list( APPEND ANDROID_SUPPORTED_ABIS ${ANDROID_SUPPORTED_ABIS_${__arch}} ) - endforeach() - unset( __uniqToolchainArchNames ) - if( NOT ANDROID_SUPPORTED_ABIS ) - message( FATAL_ERROR "No one of known Android ABIs is supported by this cmake toolchain." ) - endif() + message( FATAL_ERROR "No one of known Android ABIs is supported by this cmake toolchain." ) endif() # choose target ABI @@ -514,40 +723,41 @@ __INIT_VARIABLE( ANDROID_ABI OBSOLETE_ARM_TARGET OBSOLETE_ARM_TARGETS VALUES ${A # verify that target ABI is supported list( FIND ANDROID_SUPPORTED_ABIS "${ANDROID_ABI}" __androidAbiIdx ) if( __androidAbiIdx EQUAL -1 ) - string( REPLACE ";" "\", \"", PRINTABLE_ANDROID_SUPPORTED_ABIS "${ANDROID_SUPPORTED_ABIS}" ) + string( REPLACE ";" "\", \"" PRINTABLE_ANDROID_SUPPORTED_ABIS "${ANDROID_SUPPORTED_ABIS}" ) message( FATAL_ERROR "Specified ANDROID_ABI = \"${ANDROID_ABI}\" is not supported by this cmake toolchain or your NDK/toolchain. Supported values are: \"${PRINTABLE_ANDROID_SUPPORTED_ABIS}\" " ) endif() unset( __androidAbiIdx ) -# remember target ABI -set( ANDROID_ABI "${ANDROID_ABI}" CACHE STRING "The target ABI for Android. If arm, then armeabi-v7a is recommended for hardware floating point." FORCE ) - # set target ABI options if( ANDROID_ABI STREQUAL "x86" ) set( X86 true ) set( ANDROID_NDK_ABI_NAME "x86" ) set( ANDROID_ARCH_NAME "x86" ) set( ANDROID_ARCH_FULLNAME "x86" ) + set( ANDROID_LLVM_TRIPLE "i686-none-linux-android" ) set( CMAKE_SYSTEM_PROCESSOR "i686" ) elseif( ANDROID_ABI STREQUAL "mips" ) set( MIPS true ) set( ANDROID_NDK_ABI_NAME "mips" ) set( ANDROID_ARCH_NAME "mips" ) set( ANDROID_ARCH_FULLNAME "mipsel" ) + set( ANDROID_LLVM_TRIPLE "mipsel-none-linux-android" ) set( CMAKE_SYSTEM_PROCESSOR "mips" ) elseif( ANDROID_ABI STREQUAL "armeabi" ) set( ARMEABI true ) set( ANDROID_NDK_ABI_NAME "armeabi" ) set( ANDROID_ARCH_NAME "arm" ) set( ANDROID_ARCH_FULLNAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" ) set( CMAKE_SYSTEM_PROCESSOR "armv5te" ) elseif( ANDROID_ABI STREQUAL "armeabi-v6 with VFP" ) set( ARMEABI_V6 true ) set( ANDROID_NDK_ABI_NAME "armeabi" ) set( ANDROID_ARCH_NAME "arm" ) set( ANDROID_ARCH_FULLNAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" ) set( CMAKE_SYSTEM_PROCESSOR "armv6" ) # need always fallback to older platform set( ARMEABI true ) @@ -556,12 +766,14 @@ elseif( ANDROID_ABI STREQUAL "armeabi-v7a") set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) set( ANDROID_ARCH_NAME "arm" ) set( ANDROID_ARCH_FULLNAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) elseif( ANDROID_ABI STREQUAL "armeabi-v7a with VFPV3" ) set( ARMEABI_V7A true ) set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) set( ANDROID_ARCH_NAME "arm" ) set( ANDROID_ARCH_FULLNAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) set( VFPV3 true ) elseif( ANDROID_ABI STREQUAL "armeabi-v7a with NEON" ) @@ -569,6 +781,7 @@ elseif( ANDROID_ABI STREQUAL "armeabi-v7a with NEON" ) set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) set( ANDROID_ARCH_NAME "arm" ) set( ANDROID_ARCH_FULLNAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) set( VFPV3 true ) set( NEON true ) @@ -582,12 +795,6 @@ if( CMAKE_BINARY_DIR AND EXISTS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMa file( APPEND "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" "SET(CMAKE_SYSTEM_PROCESSOR \"${CMAKE_SYSTEM_PROCESSOR}\")\n" ) endif() -set( ANDROID_SUPPORTED_ABIS ${ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_FULLNAME}} CACHE INTERNAL "ANDROID_ABI can be changed only to one of these ABIs. Changing to any other ABI requires to reset cmake cache." ) -if( CMAKE_VERSION VERSION_GREATER "2.8" ) - list( SORT ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_FULLNAME} ) - set_property( CACHE ANDROID_ABI PROPERTY STRINGS ${ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_FULLNAME}} ) -endif() - if( ANDROID_ARCH_NAME STREQUAL "arm" AND NOT ARMEABI_V6 ) __INIT_VARIABLE( ANDROID_FORCE_ARM_BUILD OBSOLETE_FORCE_ARM VALUES OFF ) set( ANDROID_FORCE_ARM_BUILD ${ANDROID_FORCE_ARM_BUILD} CACHE BOOL "Use 32-bit ARM instructions instead of Thumb-1" FORCE ) @@ -600,11 +807,15 @@ endif() if( ANDROID_TOOLCHAIN_NAME ) list( FIND __availableToolchains "${ANDROID_TOOLCHAIN_NAME}" __toolchainIdx ) if( __toolchainIdx EQUAL -1 ) - message( FATAL_ERROR "Previously selected toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is missing. You need to remove CMakeCache.txt and rerun cmake manually to change the toolchain" ) + list( SORT __availableToolchains ) + string( REPLACE ";" "\n * " toolchains_list "${__availableToolchains}" ) + set( toolchains_list " * ${toolchains_list}") + message( FATAL_ERROR "Specified toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is missing in your NDK or broken. Please verify that your NDK is working or select another compiler toolchain. +To configure the toolchain set CMake variable ANDROID_TOOLCHAIN_NAME to one of the following values:\n${toolchains_list}\n" ) endif() list( GET __availableToolchainArchs ${__toolchainIdx} __toolchainArch ) if( NOT __toolchainArch STREQUAL ANDROID_ARCH_FULLNAME ) - message( SEND_ERROR "Previously selected toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is not able to compile binaries for the \"${ANDROID_ARCH_NAME}\" platform." ) + message( SEND_ERROR "Selected toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is not able to compile binaries for the \"${ANDROID_ARCH_NAME}\" platform." ) endif() else() set( __toolchainIdx -1 ) @@ -616,6 +827,7 @@ else() list( GET __availableToolchainArchs ${__idx} __toolchainArch ) if( __toolchainArch STREQUAL ANDROID_ARCH_FULLNAME ) list( GET __availableToolchainCompilerVersions ${__idx} __toolchainVersion ) + string( REPLACE "x" "99" __toolchainVersion "${__toolchainVersion}") if( __toolchainVersion VERSION_GREATER __toolchainMaxVersion ) set( __toolchainMaxVersion "${__toolchainVersion}" ) set( __toolchainIdx ${__idx} ) @@ -633,8 +845,7 @@ endif() list( GET __availableToolchains ${__toolchainIdx} ANDROID_TOOLCHAIN_NAME ) list( GET __availableToolchainMachines ${__toolchainIdx} ANDROID_TOOLCHAIN_MACHINE_NAME ) list( GET __availableToolchainCompilerVersions ${__toolchainIdx} ANDROID_COMPILER_VERSION ) -set( ANDROID_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" CACHE INTERNAL "Name of toolchain used" ) -set( ANDROID_COMPILER_VERSION "${ANDROID_COMPILER_VERSION}" CACHE INTERNAL "compiler version from selected toolchain" ) + unset( __toolchainIdx ) unset( __availableToolchains ) unset( __availableToolchainMachines ) @@ -644,202 +855,453 @@ unset( __availableToolchainCompilerVersions ) # choose native API level __INIT_VARIABLE( ANDROID_NATIVE_API_LEVEL ENV_ANDROID_NATIVE_API_LEVEL ANDROID_API_LEVEL ENV_ANDROID_API_LEVEL ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME} ANDROID_DEFAULT_NDK_API_LEVEL ) string( REGEX MATCH "[0-9]+" ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" ) +# adjust API level +set( __real_api_level ${ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME}} ) +foreach( __level ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) + if( NOT __level GREATER ANDROID_NATIVE_API_LEVEL AND NOT __level LESS __real_api_level ) + set( __real_api_level ${__level} ) + endif() +endforeach() +if( __real_api_level AND NOT ANDROID_NATIVE_API_LEVEL EQUAL __real_api_level ) + message( STATUS "Adjusting Android API level 'android-${ANDROID_NATIVE_API_LEVEL}' to 'android-${__real_api_level}'") + set( ANDROID_NATIVE_API_LEVEL ${__real_api_level} ) +endif() +unset(__real_api_level) # validate list( FIND ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_NATIVE_API_LEVEL}" __levelIdx ) if( __levelIdx EQUAL -1 ) - message( SEND_ERROR "Specified Android native API level (${ANDROID_NATIVE_API_LEVEL}) is not supported by your NDK/toolchain." ) + message( SEND_ERROR "Specified Android native API level 'android-${ANDROID_NATIVE_API_LEVEL}' is not supported by your NDK/toolchain." ) +else() + if( BUILD_WITH_ANDROID_NDK ) + __DETECT_NATIVE_API_LEVEL( __realApiLevel "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}/usr/include/android/api-level.h" ) + if( NOT __realApiLevel EQUAL ANDROID_NATIVE_API_LEVEL ) + message( SEND_ERROR "Specified Android API level (${ANDROID_NATIVE_API_LEVEL}) does not match to the level found (${__realApiLevel}). Probably your copy of NDK is broken." ) + endif() + unset( __realApiLevel ) + endif() + set( ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" CACHE STRING "Android API level for native code" FORCE ) + if( CMAKE_VERSION VERSION_GREATER "2.8" ) + list( SORT ANDROID_SUPPORTED_NATIVE_API_LEVELS ) + set_property( CACHE ANDROID_NATIVE_API_LEVEL PROPERTY STRINGS ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) + endif() endif() unset( __levelIdx ) + + +# remember target ABI +set( ANDROID_ABI "${ANDROID_ABI}" CACHE STRING "The target ABI for Android. If arm, then armeabi-v7a is recommended for hardware floating point." FORCE ) +if( CMAKE_VERSION VERSION_GREATER "2.8" ) + list( SORT ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_FULLNAME} ) + set_property( CACHE ANDROID_ABI PROPERTY STRINGS ${ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_FULLNAME}} ) +endif() + + +# runtime choice (STL, rtti, exceptions) +if( NOT ANDROID_STL ) + # honor legacy ANDROID_USE_STLPORT + if( DEFINED ANDROID_USE_STLPORT ) + if( ANDROID_USE_STLPORT ) + set( ANDROID_STL stlport_static ) + endif() + message( WARNING "You are using an obsolete variable ANDROID_USE_STLPORT to select the STL variant. Use -DANDROID_STL=stlport_static instead." ) + endif() + if( NOT ANDROID_STL ) + set( ANDROID_STL gnustl_static ) + endif() +endif() +set( ANDROID_STL "${ANDROID_STL}" CACHE STRING "C++ runtime" ) +set( ANDROID_STL_FORCE_FEATURES ON CACHE BOOL "automatically configure rtti and exceptions support based on C++ runtime" ) +mark_as_advanced( ANDROID_STL ANDROID_STL_FORCE_FEATURES ) + if( BUILD_WITH_ANDROID_NDK ) - __DETECT_NATIVE_API_LEVEL( __realApiLevel "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}/usr/include/android/api-level.h" ) - if( NOT __realApiLevel EQUAL ANDROID_NATIVE_API_LEVEL ) - message( SEND_ERROR "Specified Android API level (${ANDROID_NATIVE_API_LEVEL}) does not match to the level found (${__realApiLevel}). Probably your copy of NDK is broken." ) + if( NOT "${ANDROID_STL}" MATCHES "^(none|system|system_re|gabi\\+\\+_static|gabi\\+\\+_shared|stlport_static|stlport_shared|gnustl_static|gnustl_shared)$") + message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\". +The possible values are: + none -> Do not configure the runtime. + system -> Use the default minimal system C++ runtime library. + system_re -> Same as system but with rtti and exceptions. + gabi++_static -> Use the GAbi++ runtime as a static library. + gabi++_shared -> Use the GAbi++ runtime as a shared library. + stlport_static -> Use the STLport runtime as a static library. + stlport_shared -> Use the STLport runtime as a shared library. + gnustl_static -> (default) Use the GNU STL as a static library. + gnustl_shared -> Use the GNU STL as a shared library. +" ) + endif() +elseif( BUILD_WITH_STANDALONE_TOOLCHAIN ) + if( NOT "${ANDROID_STL}" MATCHES "^(none|gnustl_static|gnustl_shared)$") + message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\". +The possible values are: + none -> Do not configure the runtime. + gnustl_static -> (default) Use the GNU STL as a static library. + gnustl_shared -> Use the GNU STL as a shared library. +" ) endif() - unset( __realApiLevel ) endif() -set( ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" CACHE STRING "Android API level for native code" FORCE ) -if( CMAKE_VERSION VERSION_GREATER "2.8" ) - list( SORT ANDROID_SUPPORTED_NATIVE_API_LEVELS ) - set_property( CACHE ANDROID_NATIVE_API_LEVEL PROPERTY STRINGS ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) + +unset( ANDROID_RTTI ) +unset( ANDROID_EXCEPTIONS ) +unset( ANDROID_STL_INCLUDE_DIRS ) +unset( __libstl ) +unset( __libsupcxx ) + +if( NOT _CMAKE_IN_TRY_COMPILE AND ANDROID_NDK_RELEASE STREQUAL "r7b" AND ARMEABI_V7A AND NOT VFPV3 AND ANDROID_STL MATCHES "gnustl" ) + message( WARNING "The GNU STL armeabi-v7a binaries from NDK r7b can crash non-NEON devices. The files provided with NDK r7b were not configured properly, resulting in crashes on Tegra2-based devices and others when trying to use certain floating-point functions (e.g., cosf, sinf, expf). +You are strongly recommended to switch to another NDK release. +" ) +endif() + +if( NOT _CMAKE_IN_TRY_COMPILE AND X86 AND ANDROID_STL MATCHES "gnustl" AND ANDROID_NDK_RELEASE STREQUAL "r6" ) + message( WARNING "The x86 system header file from NDK r6 has incorrect definition for ptrdiff_t. You are recommended to upgrade to a newer NDK release or manually patch the header: +See https://android.googlesource.com/platform/development.git f907f4f9d4e56ccc8093df6fee54454b8bcab6c2 + diff --git a/ndk/platforms/android-9/arch-x86/include/machine/_types.h b/ndk/platforms/android-9/arch-x86/include/machine/_types.h + index 5e28c64..65892a1 100644 + --- a/ndk/platforms/android-9/arch-x86/include/machine/_types.h + +++ b/ndk/platforms/android-9/arch-x86/include/machine/_types.h + @@ -51,7 +51,11 @@ typedef long int ssize_t; + #endif + #ifndef _PTRDIFF_T + #define _PTRDIFF_T + -typedef long ptrdiff_t; + +# ifdef __ANDROID__ + + typedef int ptrdiff_t; + +# else + + typedef long ptrdiff_t; + +# endif + #endif +" ) endif() -# setup paths + +# setup paths and STL for standalone toolchain if( BUILD_WITH_STANDALONE_TOOLCHAIN ) set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" ) + set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" ) set( ANDROID_SYSROOT "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot" ) - set( __stlLibPath "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib" ) -endif() -if( BUILD_WITH_ANDROID_NDK ) - set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_NDK}/toolchains/${ANDROID_TOOLCHAIN_NAME}/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME}" ) - set( ANDROID_SYSROOT "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}" ) - if( ANDROID_USE_STLPORT ) - set( __stlIncludePath "${ANDROID_NDK}/sources/cxx-stl/stlport/stlport" ) - set( __stlLibPath "${ANDROID_NDK}/sources/cxx-stl/stlport/libs/${ANDROID_NDK_ABI_NAME}" ) - else() - if( EXISTS "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" ) - set( __stlIncludePath "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}/include" ) - set( __stlLibPath "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}/libs/${ANDROID_NDK_ABI_NAME}" ) + + if( NOT ANDROID_STL STREQUAL "none" ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/include/c++/${ANDROID_COMPILER_VERSION}" ) + if( NOT EXISTS "${ANDROID_STL_INCLUDE_DIRS}" ) + # old location ( pre r8c ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}" ) + endif() + if( ARMEABI_V7A AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}/bits" ) + list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}" ) + elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb/bits" ) + list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb" ) else() - set( __stlIncludePath "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/include" ) - set( __stlLibPath "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}" ) + list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" ) + endif() + # always search static GNU STL to get the location of libsupc++.a + if( ARMEABI_V7A AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libstdc++.a" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb" ) + elseif( ARMEABI_V7A AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libstdc++.a" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}" ) + elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libstdc++.a" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb" ) + elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libstdc++.a" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib" ) + endif() + if( __libstl ) + set( __libsupcxx "${__libstl}/libsupc++.a" ) + set( __libstl "${__libstl}/libstdc++.a" ) + endif() + if( NOT EXISTS "${__libsupcxx}" ) + message( FATAL_ERROR "The required libstdsupc++.a is missing in your standalone toolchain. + Usually it happens because of bug in make-standalone-toolchain.sh script from NDK r7, r7b and r7c. + You need to either upgrade to newer NDK or manually copy + $ANDROID_NDK/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a + to + ${__libsupcxx} + " ) + endif() + if( ANDROID_STL STREQUAL "gnustl_shared" ) + if( ARMEABI_V7A AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" ) + elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" ) + elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" ) + endif() endif() endif() endif() -# specify the cross compiler -set( CMAKE_C_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "gcc" ) -set( CMAKE_CXX_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "g++" ) -set( CMAKE_ASM_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "Assembler" ) -if( CMAKE_VERSION VERSION_LESS 2.8.5 ) - set( CMAKE_ASM_COMPILER_ARG1 "-c" ) -endif() -# there may be a way to make cmake deduce these TODO deduce the rest of the tools -set( CMAKE_STRIP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-strip${TOOL_OS_SUFFIX}" CACHE PATH "strip" ) -set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) -set( CMAKE_LINKER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ld${TOOL_OS_SUFFIX}" CACHE PATH "linker" ) -set( CMAKE_NM "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-nm${TOOL_OS_SUFFIX}" CACHE PATH "nm" ) -set( CMAKE_OBJCOPY "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objcopy${TOOL_OS_SUFFIX}" CACHE PATH "objcopy" ) -set( CMAKE_OBJDUMP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objdump${TOOL_OS_SUFFIX}" CACHE PATH "objdump" ) -set( CMAKE_RANLIB "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ranlib${TOOL_OS_SUFFIX}" CACHE PATH "ranlib" ) -set( _CMAKE_TOOLCHAIN_PREFIX "${ANDROID_TOOLCHAIN_MACHINE_NAME}-" ) -if( APPLE ) - find_program( CMAKE_INSTALL_NAME_TOOL NAMES install_name_tool ) - if( NOT CMAKE_INSTALL_NAME_TOOL ) - message( FATAL_ERROR "Could not find install_name_tool, please check your installation." ) +# clang +if( "${ANDROID_TOOLCHAIN_NAME}" STREQUAL "standalone-clang" ) + set( ANDROID_COMPILER_IS_CLANG 1 ) + execute_process( COMMAND "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/clang${TOOL_OS_SUFFIX}" --version OUTPUT_VARIABLE ANDROID_CLANG_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) + string( REGEX MATCH "[0-9]+[.][0-9]+" ANDROID_CLANG_VERSION "${ANDROID_CLANG_VERSION}") +elseif( "${ANDROID_TOOLCHAIN_NAME}" MATCHES "-clang3[.][0-9]?$" ) + string( REGEX MATCH "3[.][0-9]$" ANDROID_CLANG_VERSION "${ANDROID_TOOLCHAIN_NAME}") + string( REGEX REPLACE "-clang${ANDROID_CLANG_VERSION}$" "-4.6" ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" ) + if( NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}/bin/clang${TOOL_OS_SUFFIX}" ) + message( FATAL_ERROR "Could not find the Clang compiler driver" ) endif() - mark_as_advanced( CMAKE_INSTALL_NAME_TOOL ) + set( ANDROID_COMPILER_IS_CLANG 1 ) + set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) +else() + set( ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" ) + unset( ANDROID_COMPILER_IS_CLANG CACHE ) endif() -# export directories -set( ANDROID_SYSTEM_INCLUDE_DIRS "" ) -set( ANDROID_SYSTEM_LIB_DIRS "" ) +string( REPLACE "." "" _clang_name "clang${ANDROID_CLANG_VERSION}" ) +if( NOT EXISTS "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" ) + set( _clang_name "clang" ) +endif() -# setup output directories -set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "root for library output, set this to change where android libs are installed to" ) -set( CMAKE_INSTALL_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/user" CACHE STRING "path for installing" ) -if(NOT _CMAKE_IN_TRY_COMPILE) - if( EXISTS "${CMAKE_SOURCE_DIR}/jni/CMakeLists.txt" ) - set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for applications" ) +# setup paths and STL for NDK +if( BUILD_WITH_ANDROID_NDK ) + set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) + set( ANDROID_SYSROOT "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}" ) + + if( ANDROID_STL STREQUAL "none" ) + # do nothing + elseif( ANDROID_STL STREQUAL "system" ) + set( ANDROID_RTTI OFF ) + set( ANDROID_EXCEPTIONS OFF ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" ) + elseif( ANDROID_STL STREQUAL "system_re" ) + set( ANDROID_RTTI ON ) + set( ANDROID_EXCEPTIONS ON ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" ) + elseif( ANDROID_STL MATCHES "gabi" ) + if( ANDROID_NDK_RELEASE STRLESS "r7" ) + message( FATAL_ERROR "gabi++ is not awailable in your NDK. You have to upgrade to NDK r7 or newer to use gabi++.") + endif() + set( ANDROID_RTTI ON ) + set( ANDROID_EXCEPTIONS OFF ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/gabi++/include" ) + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gabi++/libs/${ANDROID_NDK_ABI_NAME}/libgabi++_static.a" ) + elseif( ANDROID_STL MATCHES "stlport" ) + if( NOT ANDROID_NDK_RELEASE STRLESS "r8d" ) + set( ANDROID_EXCEPTIONS ON ) + else() + set( ANDROID_EXCEPTIONS OFF ) + endif() + if( ANDROID_NDK_RELEASE STRLESS "r7" ) + set( ANDROID_RTTI OFF ) + else() + set( ANDROID_RTTI ON ) + endif() + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/stlport/stlport" ) + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/stlport/libs/${ANDROID_NDK_ABI_NAME}/libstlport_static.a" ) + elseif( ANDROID_STL MATCHES "gnustl" ) + set( ANDROID_EXCEPTIONS ON ) + set( ANDROID_RTTI ON ) + if( EXISTS "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" ) + if( ARMEABI_V7A AND ANDROID_COMPILER_VERSION VERSION_EQUAL "4.7" AND ANDROID_NDK_RELEASE STREQUAL "r8d" ) + # gnustl binary for 4.7 compiler is buggy :( + # TODO: look for right fix + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.6" ) + else() + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" ) + endif() + else() + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++" ) + endif() + set( ANDROID_STL_INCLUDE_DIRS "${__libstl}/include" "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/include" ) + if( EXISTS "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" ) + set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" ) + else() + set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libstdc++.a" ) + endif() else() - set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin" CACHE PATH "Output directory for applications" ) + message( FATAL_ERROR "Unknown runtime: ${ANDROID_STL}" ) + endif() + # find libsupc++.a - rtti & exceptions + if( ANDROID_STL STREQUAL "system_re" OR ANDROID_STL MATCHES "gnustl" ) + set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r8b or newer + if( NOT EXISTS "${__libsupcxx}" ) + set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r7-r8 + endif() + if( NOT EXISTS "${__libsupcxx}" ) # before r7 + if( ARMEABI_V7A ) + if( ANDROID_FORCE_ARM_BUILD ) + set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libsupc++.a" ) + else() + set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libsupc++.a" ) + endif() + elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD ) + set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libsupc++.a" ) + else() + set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libsupc++.a" ) + endif() + endif() + if( NOT EXISTS "${__libsupcxx}") + message( ERROR "Could not find libsupc++.a for a chosen platform. Either your NDK is not supported or is broken.") + endif() endif() - set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "path for android libs" ) endif() -# includes -list( APPEND ANDROID_SYSTEM_INCLUDE_DIRS "${ANDROID_SYSROOT}/usr/include" ) -if( __stlIncludePath AND EXISTS "${__stlIncludePath}" ) - list( APPEND ANDROID_SYSTEM_INCLUDE_DIRS "${__stlIncludePath}" ) -endif() -# c++ bits includes -if( __stlLibPath AND EXISTS "${__stlLibPath}/include" ) - list( APPEND ANDROID_SYSTEM_INCLUDE_DIRS "${__stlLibPath}/include" ) -endif() -if( ANDROID_ARCH_NAME STREQUAL "arm" AND EXISTS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}/thumb/bits" ) - list( APPEND ANDROID_SYSTEM_INCLUDE_DIRS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}/thumb" ) -elseif( EXISTS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}/bits" ) - list( APPEND ANDROID_SYSTEM_INCLUDE_DIRS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}" ) -elseif( ANDROID_ARCH_NAME STREQUAL "arm" AND EXISTS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb/bits" ) - list( APPEND ANDROID_SYSTEM_INCLUDE_DIRS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb" ) -elseif( EXISTS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/bits" ) - list( APPEND ANDROID_SYSTEM_INCLUDE_DIRS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" ) +# case of shared STL linkage +if( ANDROID_STL MATCHES "shared" AND DEFINED __libstl ) + string( REPLACE "_static.a" "_shared.so" __libstl "${__libstl}" ) + # TODO: check if .so file exists before the renaming endif() -# flags and definitions -if(ANDROID_SYSROOT MATCHES "[ ;\"]") - set( ANDROID_CXX_FLAGS "--sysroot=\"${ANDROID_SYSROOT}\"" ) - # quotes will break try_compile and compiler identification - message(WARNING "Your Android system root has non-alphanumeric symbols. It can break compiler features detection and the whole build.") + +# ccache support +__INIT_VARIABLE( _ndk_ccache NDK_CCACHE ENV_NDK_CCACHE ) +if( _ndk_ccache ) + if( DEFINED NDK_CCACHE AND NOT EXISTS NDK_CCACHE ) + unset( NDK_CCACHE CACHE ) + endif() + find_program( NDK_CCACHE "${_ndk_ccache}" DOC "The path to ccache binary") else() - set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) + unset( NDK_CCACHE CACHE ) endif() +unset( _ndk_ccache ) -remove_definitions( -DANDROID ) -add_definitions( -DANDROID ) + +# setup the cross-compiler +if( NOT CMAKE_C_COMPILER ) + if( NDK_CCACHE AND NOT ANDROID_SYSROOT MATCHES "[ ;\"]" ) + set( CMAKE_C_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C compiler" ) + set( CMAKE_CXX_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C++ compiler" ) + if( ANDROID_COMPILER_IS_CLANG ) + set( CMAKE_C_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") + set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") + else() + set( CMAKE_C_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") + set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") + endif() + else() + if( ANDROID_COMPILER_IS_CLANG ) + set( CMAKE_C_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") + set( CMAKE_CXX_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") + else() + set( CMAKE_C_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler" ) + set( CMAKE_CXX_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler" ) + endif() + endif() + set( CMAKE_ASM_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "assembler" ) + set( CMAKE_STRIP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-strip${TOOL_OS_SUFFIX}" CACHE PATH "strip" ) + set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) + set( CMAKE_LINKER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ld${TOOL_OS_SUFFIX}" CACHE PATH "linker" ) + set( CMAKE_NM "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-nm${TOOL_OS_SUFFIX}" CACHE PATH "nm" ) + set( CMAKE_OBJCOPY "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objcopy${TOOL_OS_SUFFIX}" CACHE PATH "objcopy" ) + set( CMAKE_OBJDUMP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objdump${TOOL_OS_SUFFIX}" CACHE PATH "objdump" ) + set( CMAKE_RANLIB "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ranlib${TOOL_OS_SUFFIX}" CACHE PATH "ranlib" ) +endif() + +set( _CMAKE_TOOLCHAIN_PREFIX "${ANDROID_TOOLCHAIN_MACHINE_NAME}-" ) +if( CMAKE_VERSION VERSION_LESS 2.8.5 ) + set( CMAKE_ASM_COMPILER_ARG1 "-c" ) +endif() +if( APPLE ) + find_program( CMAKE_INSTALL_NAME_TOOL NAMES install_name_tool ) + if( NOT CMAKE_INSTALL_NAME_TOOL ) + message( FATAL_ERROR "Could not find install_name_tool, please check your installation." ) + endif() + mark_as_advanced( CMAKE_INSTALL_NAME_TOOL ) +endif() # Force set compilers because standard identification works badly for us include( CMakeForceCompiler ) CMAKE_FORCE_C_COMPILER( "${CMAKE_C_COMPILER}" GNU ) +if( ANDROID_COMPILER_IS_CLANG ) + set( CMAKE_C_COMPILER_ID Clang) +endif() set( CMAKE_C_PLATFORM_ID Linux ) set( CMAKE_C_SIZEOF_DATA_PTR 4 ) set( CMAKE_C_HAS_ISYSROOT 1 ) set( CMAKE_C_COMPILER_ABI ELF ) CMAKE_FORCE_CXX_COMPILER( "${CMAKE_CXX_COMPILER}" GNU ) +if( ANDROID_COMPILER_IS_CLANG ) + set( CMAKE_CXX_COMPILER_ID Clang) +endif() set( CMAKE_CXX_PLATFORM_ID Linux ) set( CMAKE_CXX_SIZEOF_DATA_PTR 4 ) set( CMAKE_CXX_HAS_ISYSROOT 1 ) set( CMAKE_CXX_COMPILER_ABI ELF ) +set( CMAKE_CXX_SOURCE_FILE_EXTENSIONS cc cp cxx cpp CPP c++ C ) # force ASM compiler (required for CMake < 2.8.5) set( CMAKE_ASM_COMPILER_ID_RUN TRUE ) set( CMAKE_ASM_COMPILER_ID GNU ) set( CMAKE_ASM_COMPILER_WORKS TRUE ) set( CMAKE_ASM_COMPILER_FORCED TRUE ) set( CMAKE_COMPILER_IS_GNUASM 1) +set( CMAKE_ASM_SOURCE_FILE_EXTENSIONS s S asm ) -# NDK flags -if( ARMEABI OR ARMEABI_V7A ) - # NDK also defines -ffunction-sections -funwind-tables but they result in worse OpenCV performance - set( _CMAKE_CXX_FLAGS "-fPIC -Wno-psabi" ) - set( _CMAKE_C_FLAGS "-fPIC -Wno-psabi" ) - remove_definitions( -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ ) - add_definitions( -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ ) - # extra arm-specific flags - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fsigned-char" ) -elseif( X86 ) - set( _CMAKE_CXX_FLAGS "-funwind-tables" ) - set( _CMAKE_C_FLAGS "-funwind-tables" ) -elseif( MIPS ) - set( _CMAKE_CXX_FLAGS "-fpic -Wno-psabi -fno-strict-aliasing -finline-functions -ffunction-sections -funwind-tables -fmessage-length=0 -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers" ) - set( _CMAKE_CXX_FLAGS "-fpic -Wno-psabi -fno-strict-aliasing -finline-functions -ffunction-sections -funwind-tables -fmessage-length=0 -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers" ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fsigned-char" ) -else() - set( _CMAKE_CXX_FLAGS "" ) - set( _CMAKE_C_FLAGS "" ) -endif() +# flags and definitions +remove_definitions( -DANDROID ) +add_definitions( -DANDROID ) -if( ANDROID_USE_STLPORT ) - set( _CMAKE_CXX_FLAGS "${_CMAKE_CXX_FLAGS} -fno-rtti -fno-exceptions" ) - set( _CMAKE_C_FLAGS "${_CMAKE_C_FLAGS} -fno-exceptions" ) +if( ANDROID_SYSROOT MATCHES "[ ;\"]" ) + if( CMAKE_HOST_WIN32 ) + # try to convert path to 8.3 form + file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "@echo %~s1" ) + execute_process( COMMAND "$ENV{ComSpec}" /c "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "${ANDROID_SYSROOT}" + OUTPUT_VARIABLE __path OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE __result ERROR_QUIET ) + if( __result EQUAL 0 ) + file( TO_CMAKE_PATH "${__path}" ANDROID_SYSROOT ) + set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) + else() + set( ANDROID_CXX_FLAGS "--sysroot=\"${ANDROID_SYSROOT}\"" ) + endif() + else() + set( ANDROID_CXX_FLAGS "'--sysroot=${ANDROID_SYSROOT}'" ) + endif() + if( NOT _CMAKE_IN_TRY_COMPILE ) + # quotes can break try_compile and compiler identification + message(WARNING "Path to your Android NDK (or toolchain) has non-alphanumeric symbols.\nThe build might be broken.\n") + endif() else() - set( _CMAKE_CXX_FLAGS "${_CMAKE_CXX_FLAGS} -frtti -fexceptions" ) - set( _CMAKE_C_FLAGS "${_CMAKE_C_FLAGS} -fexceptions" ) + set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) endif() -# release and debug flags +# NDK flags if( ARMEABI OR ARMEABI_V7A ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fpic -funwind-tables" ) if( NOT ANDROID_FORCE_ARM_BUILD AND NOT ARMEABI_V6 ) - # It is recommended to use the -mthumb compiler flag to force the generation - # of 16-bit Thumb-1 instructions (the default being 32-bit ARM ones). - # O3 instead of O2/Os in release mode - like cmake sets for desktop gcc - set( _CMAKE_CXX_FLAGS_RELEASE "-mthumb -O3" ) - set( _CMAKE_C_FLAGS_RELEASE "-mthumb -O3" ) - set( _CMAKE_CXX_FLAGS_DEBUG "-marm -Os -finline-limit=64" ) - set( _CMAKE_C_FLAGS_DEBUG "-marm -Os -finline-limit=64" ) + set( ANDROID_CXX_FLAGS_RELEASE "-mthumb -fomit-frame-pointer -fno-strict-aliasing" ) + set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" ) + if( NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -finline-limit=64" ) + endif() else() # always compile ARMEABI_V6 in arm mode; otherwise there is no difference from ARMEABI - # O3 instead of O2/Os in release mode - like cmake sets for desktop gcc - set( _CMAKE_CXX_FLAGS_RELEASE "-marm -O3 -fstrict-aliasing" ) - set( _CMAKE_C_FLAGS_RELEASE "-marm -O3 -fstrict-aliasing" ) - set( _CMAKE_CXX_FLAGS_DEBUG "-marm -O0 -finline-limit=300" ) - set( _CMAKE_C_FLAGS_DEBUG "-marm -O0 -finline-limit=300" ) + set( ANDROID_CXX_FLAGS_RELEASE "-marm -fomit-frame-pointer -fstrict-aliasing" ) + set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" ) + if( NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" ) + endif() endif() elseif( X86 ) - set( _CMAKE_CXX_FLAGS_RELEASE "-O3 -fstrict-aliasing" ) - set( _CMAKE_C_FLAGS_RELEASE "-O3 -fstrict-aliasing" ) - set( _CMAKE_CXX_FLAGS_DEBUG "-O0 -finline-limit=300" ) - set( _CMAKE_C_FLAGS_DEBUG "-O0 -finline-limit=300" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) + if( NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" ) + else() + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fPIC" ) + endif() + set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" ) + set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" ) elseif( MIPS ) - set( _CMAKE_CXX_FLAGS_RELEASE "-O3 -funswitch-loops -finline-limit=300" ) - set( _CMAKE_C_FLAGS_RELEASE "-O3 -funswitch-loops -finline-limit=300" ) - set( _CMAKE_CXX_FLAGS_DEBUG "-O0 -g" ) - set( _CMAKE_C_FLAGS_DEBUG "-O0 -g" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fpic -fno-strict-aliasing -finline-functions -ffunction-sections -funwind-tables -fmessage-length=0" ) + set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer" ) + set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer" ) + if( NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers" ) + set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" ) + endif() +elseif() + set( ANDROID_CXX_FLAGS_RELEASE "" ) + set( ANDROID_CXX_FLAGS_DEBUG "" ) +endif() + +set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fsigned-char" ) # good/necessary when porting desktop libraries + +if( NOT X86 AND NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "-Wno-psabi ${ANDROID_CXX_FLAGS}" ) +endif() + +if( NOT ANDROID_COMPILER_VERSION VERSION_LESS "4.6" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -no-canonical-prefixes" ) # see https://android-review.googlesource.com/#/c/47564/ endif() -set( _CMAKE_CXX_FLAGS_RELEASE "${_CMAKE_CXX_FLAGS_RELEASE} -fomit-frame-pointer -DNDEBUG" ) -set( _CMAKE_C_FLAGS_RELEASE "${_CMAKE_C_FLAGS_RELEASE} -fomit-frame-pointer -DNDEBUG" ) -set( _CMAKE_CXX_FLAGS_DEBUG "${_CMAKE_CXX_FLAGS_DEBUG} -fno-strict-aliasing -fno-omit-frame-pointer -DDEBUG -D_DEBUG" ) -set( _CMAKE_C_FLAGS_DEBUG "${_CMAKE_C_FLAGS_DEBUG} -fno-strict-aliasing -fno-omit-frame-pointer -DDEBUG -D_DEBUG" ) # ABI-specific flags if( ARMEABI_V7A ) @@ -849,123 +1311,264 @@ if( ARMEABI_V7A ) elseif( VFPV3 ) set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3" ) else() - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfp" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3-d16" ) endif() elseif( ARMEABI_V6 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv6 -mfloat-abi=softfp -mfpu=vfp" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv6 -mfloat-abi=softfp -mfpu=vfp" ) # vfp == vfpv2 elseif( ARMEABI ) set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv5te -mtune=xscale -msoft-float" ) -elseif( X86 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS}" )#sse? endif() -# linker flags -if( NOT DEFINED __ndklibspath ) - set( __ndklibspath "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/ndklibs/${ANDROID_NDK_ABI_NAME}" ) +if( ANDROID_STL MATCHES "gnustl" AND (EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}") ) + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" ) + set( CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>" ) +else() + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "<CMAKE_CXX_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "<CMAKE_CXX_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" ) + set( CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>" ) endif() -list( APPEND ANDROID_SYSTEM_LIB_DIRS "${CMAKE_INSTALL_PREFIX}/libs/${ANDROID_NDK_ABI_NAME}" ) -set( ANDROID_LINKER_FLAGS "" ) # STL -if( ANDROID_USE_STLPORT ) - if( EXISTS "${__stlLibPath}/libstlport_static.a" ) - set( CMAKE_CXX_CREATE_SHARED_LIBRARY "<CMAKE_CXX_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES> \"${__stlLibPath}/libstlport_static.a\"") - set( CMAKE_CXX_CREATE_SHARED_MODULE "<CMAKE_CXX_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES> \"${__stlLibPath}/libstlport_static.a\"") +if( EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}" ) + if( EXISTS "${__libstl}" ) + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libstl}\"" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libstl}\"" ) + set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libstl}\"" ) endif() -else( ANDROID_USE_STLPORT ) - if( EXISTS "${__stlLibPath}/libgnustl_static.a" ) - __COPY_IF_DIFFERENT( "${__stlLibPath}/libgnustl_static.a" "${__ndklibspath}/libstdc++.a" ) - elseif( ANDROID_ARCH_NAME STREQUAL "arm" AND EXISTS "${__stlLibPath}/${CMAKE_SYSTEM_PROCESSOR}/thumb/libstdc++.a" ) - __COPY_IF_DIFFERENT( "${__stlLibPath}/${CMAKE_SYSTEM_PROCESSOR}/thumb/libstdc++.a" "${__ndklibspath}/libstdc++.a" ) - elseif( ANDROID_ARCH_NAME STREQUAL "arm" AND EXISTS "${__stlLibPath}/${CMAKE_SYSTEM_PROCESSOR}/libstdc++.a" ) - __COPY_IF_DIFFERENT( "${__stlLibPath}/${CMAKE_SYSTEM_PROCESSOR}/libstdc++.a" "${__ndklibspath}/libstdc++.a" ) - elseif( ANDROID_ARCH_NAME STREQUAL "arm" AND EXISTS "${__stlLibPath}/thumb/libstdc++.a" ) - __COPY_IF_DIFFERENT( "${__stlLibPath}/thumb/libstdc++.a" "${__ndklibspath}/libstdc++.a" ) - elseif( EXISTS "${__stlLibPath}/libstdc++.a" ) - __COPY_IF_DIFFERENT( "${__stlLibPath}/libstdc++.a" "${__ndklibspath}/libstdc++.a" ) + if( EXISTS "${__libsupcxx}" ) + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" ) + set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libsupcxx}\"" ) + # C objects: + set( CMAKE_C_CREATE_SHARED_LIBRARY "<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_C_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_C_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" ) + set( CMAKE_C_CREATE_SHARED_MODULE "<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_C_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_C_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" ) + set( CMAKE_C_LINK_EXECUTABLE "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>" ) + set( CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" ) + set( CMAKE_C_CREATE_SHARED_MODULE "${CMAKE_C_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" ) + set( CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE} \"${__libsupcxx}\"" ) endif() - if( EXISTS "${__stlLibPath}/libsupc++.a" ) - __COPY_IF_DIFFERENT( "${__stlLibPath}/libsupc++.a" "${__ndklibspath}/libsupc++.a" ) - elseif( ANDROID_ARCH_NAME STREQUAL "arm" AND EXISTS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libsupc++.a" ) - __COPY_IF_DIFFERENT( "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libsupc++.a" "${__ndklibspath}/libsupc++.a" ) - elseif( ANDROID_ARCH_NAME STREQUAL "arm" AND EXISTS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libsupc++.a" ) - __COPY_IF_DIFFERENT( "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libsupc++.a" "${__ndklibspath}/libsupc++.a" ) - elseif( ANDROID_ARCH_NAME STREQUAL "arm" AND EXISTS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libsupc++.a" ) - __COPY_IF_DIFFERENT( "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libsupc++.a" "${__ndklibspath}/libsupc++.a" ) - elseif( EXISTS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libsupc++.a" ) - __COPY_IF_DIFFERENT( "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libsupc++.a" "${__ndklibspath}/libsupc++.a" ) + if( ANDROID_STL MATCHES "gnustl" ) + if( NOT EXISTS "${ANDROID_LIBM_PATH}" ) + set( ANDROID_LIBM_PATH -lm ) + endif() + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} ${ANDROID_LIBM_PATH}" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} ${ANDROID_LIBM_PATH}" ) + set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} ${ANDROID_LIBM_PATH}" ) endif() - list( APPEND ANDROID_SYSTEM_LIB_DIRS "${__ndklibspath}" ) -endif( ANDROID_USE_STLPORT ) - -# cleanup for STL search -unset( __stlIncludePath ) -unset( __stlLibPath ) +endif() -# other linker flags +# variables controlling optional build flags +if (ANDROID_NDK_RELEASE STRLESS "r7") + # libGLESv2.so in NDK's prior to r7 refers to missing external symbols. + # So this flag option is required for all projects using OpenGL from native. + __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES ON ) +else() + __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES OFF ) +endif() __INIT_VARIABLE( ANDROID_NO_UNDEFINED OBSOLETE_NO_UNDEFINED VALUES ON ) -set( ANDROID_NO_UNDEFINED ${ANDROID_NO_UNDEFINED} CACHE BOOL "Show all undefined symbols as linker errors" FORCE ) -mark_as_advanced( ANDROID_NO_UNDEFINED ) -if( ANDROID_NO_UNDEFINED ) - set( ANDROID_LINKER_FLAGS "-Wl,--no-undefined ${ANDROID_LINKER_FLAGS}" ) +__INIT_VARIABLE( ANDROID_FUNCTION_LEVEL_LINKING VALUES ON ) +__INIT_VARIABLE( ANDROID_GOLD_LINKER VALUES ON ) +__INIT_VARIABLE( ANDROID_NOEXECSTACK VALUES ON ) +__INIT_VARIABLE( ANDROID_RELRO VALUES ON ) + +set( ANDROID_NO_UNDEFINED ${ANDROID_NO_UNDEFINED} CACHE BOOL "Show all undefined symbols as linker errors" ) +set( ANDROID_SO_UNDEFINED ${ANDROID_SO_UNDEFINED} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) +set( ANDROID_FUNCTION_LEVEL_LINKING ${ANDROID_FUNCTION_LEVEL_LINKING} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) +set( ANDROID_GOLD_LINKER ${ANDROID_GOLD_LINKER} CACHE BOOL "Enables gold linker (only avaialble for NDK r8b for ARM and x86 architectures on linux-86 and darwin-x86 hosts)" ) +set( ANDROID_NOEXECSTACK ${ANDROID_NOEXECSTACK} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) +set( ANDROID_RELRO ${ANDROID_RELRO} CACHE BOOL "Enables RELRO - a memory corruption mitigation technique" ) +mark_as_advanced( ANDROID_NO_UNDEFINED ANDROID_SO_UNDEFINED ANDROID_FUNCTION_LEVEL_LINKING ANDROID_GOLD_LINKER ANDROID_NOEXECSTACK ANDROID_RELRO ) + +# linker flags +set( ANDROID_LINKER_FLAGS "" ) + +if( ARMEABI_V7A ) + # this is *required* to use the following linker flags that routes around + # a CPU bug in some Cortex-A8 implementations: + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--fix-cortex-a8" ) endif() -if (ANDROID_NDK MATCHES "-r[56].?$") - # libGLESv2.so in NDK's prior to r7 refers to exteranal symbols. So this flag option is required for all projects using OpenGL from native. - __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES ON ) -else() - __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES OFF ) +if( ANDROID_NO_UNDEFINED ) + if( MIPS ) + # there is some sysroot-related problem in mips linker... + if( NOT ANDROID_SYSROOT MATCHES "[ ;\"]" ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined -Wl,-rpath-link,${ANDROID_SYSROOT}/usr/lib" ) + endif() + else() + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined" ) + endif() endif() -set( ANDROID_SO_UNDEFINED ${ANDROID_SO_UNDEFINED} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" FORCE ) -mark_as_advanced( ANDROID_SO_UNDEFINED ) if( ANDROID_SO_UNDEFINED ) set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-allow-shlib-undefined" ) endif() -__INIT_VARIABLE( ANDROID_FUNCTION_LEVEL_LINKING VALUES ON ) -set( ANDROID_FUNCTION_LEVEL_LINKING ON CACHE BOOL "Allows or disallows undefined symbols in shared libraries" FORCE ) -mark_as_advanced( ANDROID_FUNCTION_LEVEL_LINKING ) if( ANDROID_FUNCTION_LEVEL_LINKING ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fdata-sections -ffunction-sections" ) - set( ANDROID_LINKER_FLAGS "-Wl,--gc-sections ${ANDROID_LINKER_FLAGS}" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fdata-sections -ffunction-sections" ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--gc-sections" ) endif() -if( ARMEABI_V7A ) - # this is *required* to use the following linker flags that routes around - # a CPU bug in some Cortex-A8 implementations: - set( ANDROID_LINKER_FLAGS "-Wl,--fix-cortex-a8 ${ANDROID_LINKER_FLAGS}" ) +if( ANDROID_COMPILER_VERSION VERSION_EQUAL "4.6" ) + if( ANDROID_GOLD_LINKER AND (CMAKE_HOST_UNIX OR ANDROID_NDK_RELEASE STRGREATER "r8b") AND (ARMEABI OR ARMEABI_V7A OR X86) ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=gold" ) + elseif( ANDROID_NDK_RELEASE STRGREATER "r8b") + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=bfd" ) + elseif( ANDROID_NDK_RELEASE STREQUAL "r8b" AND ARMEABI AND NOT _CMAKE_IN_TRY_COMPILE ) + message( WARNING "The default bfd linker from arm GCC 4.6 toolchain can fail with 'unresolvable R_ARM_THM_CALL relocation' error message. See https://code.google.com/p/android/issues/detail?id=35342 + On Linux and OS X host platform you can workaround this problem using gold linker (default). + Rerun cmake with -DANDROID_GOLD_LINKER=ON option in case of problems. +" ) + endif() +endif() # version 4.6 + +if( ANDROID_NOEXECSTACK ) + if( ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Xclang -mnoexecstack" ) + else() + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Wa,--noexecstack" ) + endif() + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,noexecstack" ) +endif() + +if( ANDROID_RELRO ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now" ) +endif() + +if( ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "-Qunused-arguments ${ANDROID_CXX_FLAGS}" ) + if( ARMEABI_V7A AND NOT ANDROID_FORCE_ARM_BUILD ) + set( ANDROID_CXX_FLAGS_RELEASE "-target thumbv7-none-linux-androideabi ${ANDROID_CXX_FLAGS_RELEASE}" ) + set( ANDROID_CXX_FLAGS_DEBUG "-target ${ANDROID_LLVM_TRIPLE} ${ANDROID_CXX_FLAGS_DEBUG}" ) + else() + set( ANDROID_CXX_FLAGS "-target ${ANDROID_LLVM_TRIPLE} ${ANDROID_CXX_FLAGS}" ) + endif() + if( BUILD_WITH_ANDROID_NDK ) + set( ANDROID_CXX_FLAGS "-gcc-toolchain ${ANDROID_TOOLCHAIN_ROOT} ${ANDROID_CXX_FLAGS}" ) + endif() endif() # cache flags -set( CMAKE_CXX_FLAGS "${_CMAKE_CXX_FLAGS}" CACHE STRING "c++ flags" ) -set( CMAKE_C_FLAGS "${_CMAKE_C_FLAGS}" CACHE STRING "c flags" ) -set( CMAKE_CXX_FLAGS_RELEASE "${_CMAKE_CXX_FLAGS_RELEASE}" CACHE STRING "c++ Release flags" ) -set( CMAKE_C_FLAGS_RELEASE "${_CMAKE_C_FLAGS_RELEASE}" CACHE STRING "c Release flags" ) -set( CMAKE_CXX_FLAGS_DEBUG "${_CMAKE_CXX_FLAGS_DEBUG}" CACHE STRING "c++ Debug flags" ) -set( CMAKE_C_FLAGS_DEBUG "${_CMAKE_C_FLAGS_DEBUG}" CACHE STRING "c Debug flags" ) -set( CMAKE_SHARED_LINKER_FLAGS "" CACHE STRING "linker flags" ) -set( CMAKE_MODULE_LINKER_FLAGS "" CACHE STRING "linker flags" ) -set( CMAKE_EXE_LINKER_FLAGS "-Wl,-z,nocopyreloc" CACHE STRING "linker flags" ) - -include_directories( SYSTEM ${ANDROID_SYSTEM_INCLUDE_DIRS} ) -link_directories( ${ANDROID_SYSTEM_LIB_DIRS} ) +set( CMAKE_CXX_FLAGS "" CACHE STRING "c++ flags" ) +set( CMAKE_C_FLAGS "" CACHE STRING "c flags" ) +set( CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c++ Release flags" ) +set( CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c Release flags" ) +set( CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c++ Debug flags" ) +set( CMAKE_C_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c Debug flags" ) +set( CMAKE_SHARED_LINKER_FLAGS "" CACHE STRING "shared linker flags" ) +set( CMAKE_MODULE_LINKER_FLAGS "" CACHE STRING "module linker flags" ) +set( CMAKE_EXE_LINKER_FLAGS "-Wl,-z,nocopyreloc" CACHE STRING "executable linker flags" ) + +# put flags to cache (for debug purpose only) +set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS}" CACHE INTERNAL "Android specific c/c++ flags" ) +set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE}" CACHE INTERNAL "Android specific c/c++ Release flags" ) +set( ANDROID_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG}" CACHE INTERNAL "Android specific c/c++ Debug flags" ) +set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS}" CACHE INTERNAL "Android specific c/c++ linker flags" ) # finish flags -set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS}" CACHE INTERNAL "Extra Android compiler flags") -set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS}" CACHE INTERNAL "Extra Android linker flags") set( CMAKE_CXX_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_CXX_FLAGS}" ) set( CMAKE_C_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_C_FLAGS}" ) -if( MIPS AND BUILD_WITH_ANDROID_NDK ) - set( CMAKE_SHARED_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK}/toolchains/${ANDROID_TOOLCHAIN_NAME}/mipself.xsc ${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}" ) - set( CMAKE_MODULE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK}/toolchains/${ANDROID_TOOLCHAIN_NAME}/mipself.xsc ${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}" ) - set( CMAKE_EXE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK}/toolchains/${ANDROID_TOOLCHAIN_NAME}/mipself.x ${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" ) -else() - set( CMAKE_SHARED_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}" ) - set( CMAKE_MODULE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}" ) - set( CMAKE_EXE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" ) +set( CMAKE_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELEASE}" ) +set( CMAKE_C_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELEASE}" ) +set( CMAKE_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_DEBUG}" ) +set( CMAKE_C_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_C_FLAGS_DEBUG}" ) +set( CMAKE_SHARED_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}" ) +set( CMAKE_MODULE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}" ) +set( CMAKE_EXE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" ) + +if( MIPS AND BUILD_WITH_ANDROID_NDK AND ANDROID_NDK_RELEASE STREQUAL "r8" ) + set( CMAKE_SHARED_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_SHARED_LINKER_FLAGS}" ) + set( CMAKE_MODULE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_MODULE_LINKER_FLAGS}" ) + set( CMAKE_EXE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.x ${CMAKE_EXE_LINKER_FLAGS}" ) endif() +# configure rtti +if( DEFINED ANDROID_RTTI AND ANDROID_STL_FORCE_FEATURES ) + if( ANDROID_RTTI ) + set( CMAKE_CXX_FLAGS "-frtti ${CMAKE_CXX_FLAGS}" ) + else() + set( CMAKE_CXX_FLAGS "-fno-rtti ${CMAKE_CXX_FLAGS}" ) + endif() +endif() + +# configure exceptios +if( DEFINED ANDROID_EXCEPTIONS AND ANDROID_STL_FORCE_FEATURES ) + if( ANDROID_EXCEPTIONS ) + set( CMAKE_CXX_FLAGS "-fexceptions ${CMAKE_CXX_FLAGS}" ) + set( CMAKE_C_FLAGS "-fexceptions ${CMAKE_C_FLAGS}" ) + else() + set( CMAKE_CXX_FLAGS "-fno-exceptions ${CMAKE_CXX_FLAGS}" ) + set( CMAKE_C_FLAGS "-fno-exceptions ${CMAKE_C_FLAGS}" ) + endif() +endif() + +# global includes and link directories +include_directories( SYSTEM "${ANDROID_SYSROOT}/usr/include" ${ANDROID_STL_INCLUDE_DIRS} ) +get_filename_component(__android_install_path "${CMAKE_INSTALL_PREFIX}/libs/${ANDROID_NDK_ABI_NAME}" ABSOLUTE) # avoid CMP0015 policy warning +link_directories( "${__android_install_path}" ) + +# detect if need link crtbegin_so.o explicitly +if( NOT DEFINED ANDROID_EXPLICIT_CRT_LINK ) + set( __cmd "${CMAKE_CXX_CREATE_SHARED_LIBRARY}" ) + string( REPLACE "<CMAKE_CXX_COMPILER>" "${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1}" __cmd "${__cmd}" ) + string( REPLACE "<CMAKE_C_COMPILER>" "${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}" __cmd "${__cmd}" ) + string( REPLACE "<CMAKE_SHARED_LIBRARY_CXX_FLAGS>" "${CMAKE_CXX_FLAGS}" __cmd "${__cmd}" ) + string( REPLACE "<LANGUAGE_COMPILE_FLAGS>" "" __cmd "${__cmd}" ) + string( REPLACE "<LINK_FLAGS>" "${CMAKE_SHARED_LINKER_FLAGS}" __cmd "${__cmd}" ) + string( REPLACE "<CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS>" "-shared" __cmd "${__cmd}" ) + string( REPLACE "<CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG>" "" __cmd "${__cmd}" ) + string( REPLACE "<TARGET_SONAME>" "" __cmd "${__cmd}" ) + string( REPLACE "<TARGET>" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/toolchain_crtlink_test.so" __cmd "${__cmd}" ) + string( REPLACE "<OBJECTS>" "\"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" __cmd "${__cmd}" ) + string( REPLACE "<LINK_LIBRARIES>" "" __cmd "${__cmd}" ) + separate_arguments( __cmd ) + foreach( __var ANDROID_NDK ANDROID_NDK_TOOLCHAINS_PATH ANDROID_STANDALONE_TOOLCHAIN ) + if( ${__var} ) + set( __tmp "${${__var}}" ) + separate_arguments( __tmp ) + string( REPLACE "${__tmp}" "${${__var}}" __cmd "${__cmd}") + endif() + endforeach() + string( REPLACE "'" "" __cmd "${__cmd}" ) + string( REPLACE "\"" "" __cmd "${__cmd}" ) + execute_process( COMMAND ${__cmd} RESULT_VARIABLE __cmd_result OUTPUT_QUIET ERROR_QUIET ) + if( __cmd_result EQUAL 0 ) + set( ANDROID_EXPLICIT_CRT_LINK ON ) + else() + set( ANDROID_EXPLICIT_CRT_LINK OFF ) + endif() +endif() + +if( ANDROID_EXPLICIT_CRT_LINK ) + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" ) +endif() + +# setup output directories +set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "root for library output, set this to change where android libs are installed to" ) +set( CMAKE_INSTALL_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/user" CACHE STRING "path for installing" ) + +if(NOT _CMAKE_IN_TRY_COMPILE) + if( EXISTS "${CMAKE_SOURCE_DIR}/jni/CMakeLists.txt" ) + set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for applications" ) + else() + set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin" CACHE PATH "Output directory for applications" ) + endif() + set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "path for android libs" ) +endif() + +# copy shaed stl library to build directory +if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" ) + get_filename_component( __libstlname "${__libstl}" NAME ) + execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess ) + if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}") + message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" ) + endif() + unset( __fileCopyProcess ) + unset( __libstlname ) +endif() + + # set these global flags for cmake client scripts to change behavior set( ANDROID True ) set( BUILD_ANDROID True ) @@ -1027,11 +1630,13 @@ macro( ANDROID_GET_ABI_RAWNAME TOOLCHAIN_FLAG VAR ) if( "${TOOLCHAIN_FLAG}" STREQUAL "ARMEABI" ) set( ${VAR} "armeabi" ) elseif( "${TOOLCHAIN_FLAG}" STREQUAL "ARMEABI_V7A" ) - set( ${VAR} "armeabi-v7a" ) + set( ${VAR} "armeabi-v7a" ) elseif( "${TOOLCHAIN_FLAG}" STREQUAL "X86" ) - set( ${VAR} "x86" ) + set( ${VAR} "x86" ) + elseif( "${TOOLCHAIN_FLAG}" STREQUAL "MIPS" ) + set( ${VAR} "mips" ) else() - set( ${VAR} "unknown" ) + set( ${VAR} "unknown" ) endif() endmacro() @@ -1039,7 +1644,26 @@ endmacro() # export toolchain settings for the try_compile() command if( NOT PROJECT_NAME STREQUAL "CMAKE_TRY_COMPILE" ) set( __toolchain_config "") - foreach( __var ANDROID_ABI ANDROID_FORCE_ARM_BUILD ANDROID_NATIVE_API_LEVEL ANDROID_NO_UNDEFINED ANDROID_SO_UNDEFINED ANDROID_SET_OBSOLETE_VARIABLES LIBRARY_OUTPUT_PATH_ROOT ANDROID_USE_STLPORT ANDROID_FORBID_SYGWIN ANDROID_NDK ANDROID_STANDALONE_TOOLCHAIN ANDROID_FUNCTION_LEVEL_LINKING __ndklibspath ) + foreach( __var NDK_CCACHE LIBRARY_OUTPUT_PATH_ROOT ANDROID_FORBID_SYGWIN ANDROID_SET_OBSOLETE_VARIABLES + ANDROID_NDK_HOST_X64 + ANDROID_NDK + ANDROID_NDK_LAYOUT + ANDROID_STANDALONE_TOOLCHAIN + ANDROID_TOOLCHAIN_NAME + ANDROID_ABI + ANDROID_NATIVE_API_LEVEL + ANDROID_STL + ANDROID_STL_FORCE_FEATURES + ANDROID_FORCE_ARM_BUILD + ANDROID_NO_UNDEFINED + ANDROID_SO_UNDEFINED + ANDROID_FUNCTION_LEVEL_LINKING + ANDROID_GOLD_LINKER + ANDROID_NOEXECSTACK + ANDROID_RELRO + ANDROID_LIBM_PATH + ANDROID_EXPLICIT_CRT_LINK + ) if( DEFINED ${__var} ) if( "${__var}" MATCHES " ") set( __toolchain_config "${__toolchain_config}set( ${__var} \"${${__var}}\" CACHE INTERNAL \"\" )\n" ) @@ -1050,7 +1674,19 @@ if( NOT PROJECT_NAME STREQUAL "CMAKE_TRY_COMPILE" ) endforeach() file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android.toolchain.config.cmake" "${__toolchain_config}" ) unset( __toolchain_config ) - unset( __ndklibspath ) +endif() + + +# force cmake to produce / instead of \ in build commands for Ninja generator +if( CMAKE_GENERATOR MATCHES "Ninja" AND CMAKE_HOST_WIN32 ) + # it is a bad hack after all + # CMake generates Ninja makefiles with UNIX paths only if it thinks that we are going to build with MinGW + set( CMAKE_COMPILER_IS_MINGW TRUE ) # tell CMake that we are MinGW + set( CMAKE_CROSSCOMPILING TRUE ) # stop recursion + enable_language( C ) + enable_language( CXX ) + # unset( CMAKE_COMPILER_IS_MINGW ) # can't unset because CMake does not convert back-slashes in response files without it + unset( MINGW ) endif() @@ -1067,24 +1703,31 @@ endif() # Variables controlling behavior or set by cmake toolchain: # ANDROID_ABI : "armeabi-v7a" (default), "armeabi", "armeabi-v7a with NEON", "armeabi-v7a with VFPV3", "armeabi-v6 with VFP", "x86", "mips" # ANDROID_NATIVE_API_LEVEL : 3,4,5,8,9,14 (depends on NDK version) -# ANDROID_SET_OBSOLETE_VARIABLES : ON/OFF -# ANDROID_USE_STLPORT : OFF/ON - EXPERIMENTAL!!! +# ANDROID_STL : gnustl_static/gnustl_shared/stlport_static/stlport_shared/gabi++_static/gabi++_shared/system_re/system/none # ANDROID_FORBID_SYGWIN : ON/OFF # ANDROID_NO_UNDEFINED : ON/OFF # ANDROID_SO_UNDEFINED : OFF/ON (default depends on NDK version) # ANDROID_FUNCTION_LEVEL_LINKING : ON/OFF -# Variables that takes effect only at first run: +# ANDROID_GOLD_LINKER : ON/OFF +# ANDROID_NOEXECSTACK : ON/OFF +# ANDROID_RELRO : ON/OFF # ANDROID_FORCE_ARM_BUILD : ON/OFF -# LIBRARY_OUTPUT_PATH_ROOT : <any valid path> +# ANDROID_STL_FORCE_FEATURES : ON/OFF +# ANDROID_SET_OBSOLETE_VARIABLES : ON/OFF # Can be set only at the first run: # ANDROID_NDK # ANDROID_STANDALONE_TOOLCHAIN -# ANDROID_TOOLCHAIN_NAME : "arm-linux-androideabi-4.4.3" or "arm-linux-androideabi-4.6" or "mipsel-linux-android-4.4.3" or "mipsel-linux-android-4.6" or "x86-4.4.3" or "x86-4.6" +# ANDROID_TOOLCHAIN_NAME : the NDK name of compiler toolchain +# ANDROID_NDK_HOST_X64 : try to use x86_64 toolchain (default for x64 host systems) +# ANDROID_NDK_LAYOUT : the inner NDK structure (RELEASE, LINARO, ANDROID) +# LIBRARY_OUTPUT_PATH_ROOT : <any valid path> +# NDK_CCACHE : <path to your ccache executable> # Obsolete: # ANDROID_API_LEVEL : superseded by ANDROID_NATIVE_API_LEVEL # ARM_TARGET : superseded by ANDROID_ABI # ARM_TARGETS : superseded by ANDROID_ABI (can be set only) # ANDROID_NDK_TOOLCHAIN_ROOT : superseded by ANDROID_STANDALONE_TOOLCHAIN (can be set only) +# ANDROID_USE_STLPORT : superseded by ANDROID_STL=stlport_static # ANDROID_LEVEL : superseded by ANDROID_NATIVE_API_LEVEL (completely removed) # # Primary read-only variables: @@ -1095,16 +1738,17 @@ endif() # NEON : TRUE if NEON unit is enabled # VFPV3 : TRUE if VFP version 3 is enabled # X86 : TRUE if configured for x86 +# MIPS : TRUE if configured for mips # BUILD_ANDROID : always TRUE # BUILD_WITH_ANDROID_NDK : TRUE if NDK is used # BUILD_WITH_STANDALONE_TOOLCHAIN : TRUE if standalone toolchain is used # ANDROID_NDK_HOST_SYSTEM_NAME : "windows", "linux-x86" or "darwin-x86" depending on host platform -# ANDROID_NDK_ABI_NAME : "armeabi", "armeabi-v7a" or "x86" depending on ANDROID_ABI +# ANDROID_NDK_ABI_NAME : "armeabi", "armeabi-v7a", "x86" or "mips" depending on ANDROID_ABI +# ANDROID_NDK_RELEASE : one of r5, r5b, r5c, r6, r6b, r7, r7b, r7c, r8, r8b, r8c, r8d, r8e, r9, r9b, r9c; set only for NDK # ANDROID_ARCH_NAME : "arm" or "x86" or "mips" depending on ANDROID_ABI -# TOOL_OS_SUFFIX : "" or ".exe" depending on host platform # ANDROID_SYSROOT : path to the compiler sysroot -# ANDROID_SYSTEM_INCLUDE_DIRS -# ANDROID_SYSTEM_LIB_DIRS +# TOOL_OS_SUFFIX : "" or ".exe" depending on host platform +# ANDROID_COMPILER_IS_CLANG : TRUE if clang compiler is used # Obsolete: # ARMEABI_NDK_NAME : superseded by ANDROID_NDK_ABI_NAME # @@ -1114,7 +1758,14 @@ endif() # ANDROID_SUPPORTED_ABIS : list of currently allowed values for ANDROID_ABI # ANDROID_TOOLCHAIN_MACHINE_NAME : "arm-linux-androideabi", "arm-eabi" or "i686-android-linux" # ANDROID_TOOLCHAIN_ROOT : path to the top level of toolchain (standalone or placed inside NDK) +# ANDROID_CLANG_TOOLCHAIN_ROOT : path to clang tools # ANDROID_SUPPORTED_NATIVE_API_LEVELS : list of native API levels found inside NDK +# ANDROID_STL_INCLUDE_DIRS : stl include paths +# ANDROID_RTTI : if rtti is enabled by the runtime +# ANDROID_EXCEPTIONS : if exceptions are enabled by the runtime +# ANDROID_GCC_TOOLCHAIN_NAME : read-only, differs from ANDROID_TOOLCHAIN_NAME only if clang is used +# ANDROID_CLANG_VERSION : version of clang compiler if clang is used +# ANDROID_LIBM_PATH : path to libm.so (set to something like $(TOP)/out/target/product/<product_name>/obj/lib/libm.so) to workaround unresolved `sincos` # # Defaults: # ANDROID_DEFAULT_NDK_API_LEVEL @@ -1122,4 +1773,4 @@ endif() # ANDROID_NDK_SEARCH_PATHS # ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH # ANDROID_SUPPORTED_ABIS_${ARCH} -# ANDROID_SUPPORTED_NDK_VERSIONS +# ANDROID_SUPPORTED_NDK_VERSIONS
\ No newline at end of file diff --git a/contrib/local/compress_and_upload_deps.sh b/contrib/local/compress_and_upload_deps.sh index 746bd84..5a2cea3 100755 --- a/contrib/local/compress_and_upload_deps.sh +++ b/contrib/local/compress_and_upload_deps.sh @@ -40,7 +40,11 @@ for FILE in ${PLATFORMS}; do if [[ "$PLATFORM" == *windows* ]]; then tar cvzf uscxml-prebuilt-${PLATFORM}.tgz --exclude='*/.DS_Store' --exclude='VERSION.txt' ${FILE} else - tar cvzf uscxml-prebuilt-${PLATFORM}.tgz --exclude='*/.DS_Store' --exclude='VERSION.txt' --exclude='lib/*_d.a' ${FILE} + if [[ "$PLATFORM" == *darwin* ]]; then + tar cvzf uscxml-prebuilt-${PLATFORM}.tgz --exclude='*/.DS_Store' --exclude='VERSION.txt' --exclude='lib/libv8*' --exclude='lib/*_d.a' ${FILE} + else + tar cvzf uscxml-prebuilt-${PLATFORM}.tgz --exclude='*/.DS_Store' --exclude='VERSION.txt' --exclude='lib/*_d.a' ${FILE} + fi fi scp uscxml-prebuilt-${PLATFORM}.tgz ${USCXML_PREBUILT_HOST}:${USCXML_PREBUILT_PATH}/${VERSION} rm uscxml-prebuilt-${PLATFORM}.tgz diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 6334065..2e867ba 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -258,6 +258,7 @@ InterpreterImpl::InterpreterImpl() { _sendQueue = NULL; _parentQueue = NULL; _running = false; + _destroyed = false; _done = true; _isInitialized = false; _httpServlet = NULL; @@ -403,12 +404,18 @@ InterpreterImpl::~InterpreterImpl() { _running = false; // tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); if (_thread) { - // unblock event queue - Event event; - event.name = "unblock.and.die"; - receive(event); - _thread->join(); - delete(_thread); + if (_thread->get_id() != tthread::this_thread::get_id()) { + // unblock event queue + Event event; + event.name = "unblock.and.die"; + receive(event); + + _thread->join(); + delete(_thread); + } else { + // this can happen with a shared_from_this at an interpretermonitor + _destroyed = true; + } } if (_sendQueue) delete _sendQueue; @@ -422,6 +429,11 @@ void InterpreterImpl::start() { _thread = new tthread::thread(InterpreterImpl::run, this); } +void InterpreterImpl::stop() { + _running = false; +} + + void InterpreterImpl::run(void* instance) { try { ((InterpreterImpl*)instance)->interpret(); @@ -1116,9 +1128,24 @@ void InterpreterImpl::invoke(const Arabica::DOM::Node<std::string>& element) { invoker.setType(invokeReq.type); invoker.setInterpreter(this); _invokers[invokeReq.invokeid] = invoker; - LOG(INFO) << "Added invoker " << invokeReq.type << " at " << invokeReq.invokeid; try { + + // --- MONITOR: beforeInvoking ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeInvoking(shared_from_this(), Element<std::string>(element), invokeReq.invokeid); + } USCXML_MONITOR_CATCH_BLOCK(beforeInvoking) + } + invoker.invoke(invokeReq); + LOG(INFO) << "Added invoker " << invokeReq.type << " at " << invokeReq.invokeid; + + // --- MONITOR: afterInvoking ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->afterInvoking(shared_from_this(), Element<std::string>(element), invokeReq.invokeid); + } USCXML_MONITOR_CATCH_BLOCK(afterInvoking) + } // this is out of draft but so useful to know when an invoker started // Event invSuccess; @@ -1166,9 +1193,24 @@ void InterpreterImpl::cancelInvoke(const Arabica::DOM::Node<std::string>& elemen } catch (Event e) { LOG(ERROR) << "Syntax when removing invoker:" << std::endl << e << std::endl; } - } + + // --- MONITOR: beforeUninvoking ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeUninvoking(shared_from_this(), Element<std::string>(element), invokeId); + } USCXML_MONITOR_CATCH_BLOCK(beforeUninvoking) + } + _invokers.erase(invokeId); + + // --- MONITOR: afterUninvoking ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->afterUninvoking(shared_from_this(), Element<std::string>(element), invokeId); + } USCXML_MONITOR_CATCH_BLOCK(beforeUninvoking) + } + } else { LOG(ERROR) << "Cannot cancel invoke for id " << invokeId << ": no such invokation"; } diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index d2e63e9..94c5d74 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -48,6 +48,18 @@ #include "uscxml/server/InterpreterServlet.h" +#define USCXML_MONITOR_CATCH_BLOCK(callback)\ +catch (Event e) {\ + LOG(ERROR) << "Syntax error when calling " #callback " on monitors: " << std::endl << e << std::endl;\ +} catch (boost::bad_weak_ptr e) {\ + LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl;\ +} catch (...) {\ + LOG(ERROR) << "An exception occured when calling " #callback " on monitors";\ +}\ +if (_destroyed) {\ + throw boost::bad_weak_ptr();\ +} + namespace uscxml { class HTTPServletInvoker; @@ -139,6 +151,7 @@ public: virtual ~InterpreterImpl(); void start(); + void stop(); static void run(void*); void join() { if (_thread != NULL) _thread->join(); @@ -288,6 +301,7 @@ public: static std::list<std::string> tokenizeIdRefs(const std::string& idRefs); static std::string spaceNormalize(const std::string& text); + static bool nameMatch(const std::string& transitionEvent, const std::string& event); bool isInEmbeddedDocument(const Arabica::DOM::Node<std::string>& node); bool isInitial(const Arabica::DOM::Node<std::string>& state); @@ -337,6 +351,7 @@ protected: bool _running; bool _done; + bool _destroyed; // see comment in destructor bool _isInitialized; InterpreterImpl::Binding _binding; Arabica::XPath::NodeSet<std::string> _configuration; @@ -381,7 +396,6 @@ protected: void internalDoneSend(const Arabica::DOM::Node<std::string>& state); static void delayedSend(void* userdata, std::string eventName); - static bool nameMatch(const std::string& transitionEvent, const std::string& event); bool hasConditionMatch(const Arabica::DOM::Node<std::string>& conditional); bool isInFinalState(const Arabica::DOM::Node<std::string>& state); bool parentIsScxmlState(const Arabica::DOM::Node<std::string>& state); @@ -417,7 +431,7 @@ public: static Interpreter fromURI(const std::string& uri); static Interpreter fromInputSource(Arabica::SAX::InputSource<std::string>& source); - Interpreter() : _impl() {} + Interpreter() : _impl() {} // the empty, invalid interpreter Interpreter(boost::shared_ptr<InterpreterImpl> const impl) : _impl(impl) { } Interpreter(const Interpreter& other) : _impl(other._impl) { } virtual ~Interpreter() {}; @@ -442,6 +456,9 @@ public: void start() { return _impl->start(); } + void stop() { + return _impl->stop(); + } void join() { return _impl->join(); }; @@ -685,15 +702,35 @@ protected: class USCXML_API InterpreterMonitor { public: virtual ~InterpreterMonitor() {} + + virtual void beforeProcessingEvent(Interpreter interpreter, const Event& event) {} + virtual void beforeMicroStep(Interpreter interpreter) {} + + virtual void beforeExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) {} + virtual void afterExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) {} + + virtual void beforeUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) {} + virtual void afterUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) {} + + virtual void beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition) {} + virtual void afterTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition) {} + + virtual void beforeEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) {} + virtual void afterEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) {} + + virtual void beforeInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) {} + virtual void afterInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) {} + + virtual void afterMicroStep(Interpreter interpreter) {} + + virtual void beforeExecutingContent(Interpreter interpreter, const Arabica::DOM::Element<std::string>& element) {} + virtual void afterExecutingContent(Interpreter interpreter, const Arabica::DOM::Element<std::string>& element) {} + virtual void onStableConfiguration(Interpreter interpreter) {} + virtual void beforeCompletion(Interpreter interpreter) {} virtual void afterCompletion(Interpreter interpreter) {} - virtual void beforeMicroStep(Interpreter interpreter) {} - virtual void beforeTakingTransitions(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& transitions) {} - virtual void beforeEnteringStates(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& statesToEnter) {} - virtual void afterEnteringStates(Interpreter interpreter) {} - virtual void beforeExitingStates(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& statesToExit) {} - virtual void afterExitingStates(Interpreter interpreter) {} + }; } diff --git a/src/uscxml/Message.cpp b/src/uscxml/Message.cpp index e8ae98c..aeb0027 100644 --- a/src/uscxml/Message.cpp +++ b/src/uscxml/Message.cpp @@ -721,27 +721,38 @@ std::string Data::toJSON(const Data& data) { } os << "]"; } else if (data.atom.size() > 0) { + // empty string is handled below if (data.type == Data::VERBATIM) { os << "\""; for (int i = 0; i < data.atom.size(); i++) { // escape string - if (data.atom[i] == '"') { + if (false) { + } else if (data.atom[i] == '"') { os << '\\'; + os << data.atom[i]; + } else if (data.atom[i] == '\n') { + os << "\\n"; + } else { + os << data.atom[i]; } - os << data.atom[i]; } os << "\""; } else { os << data.atom; } } else if (data.node) { + std::ostringstream xmlSerSS; + xmlSerSS << data.node; + std::string xmlSer = xmlSerSS.str(); + boost::replace_all(xmlSer, "\"", "\\\""); + boost::replace_all(xmlSer, "\n", "\\n"); + os << "\"" << xmlSer << "\""; + } else { if (data.type == Data::VERBATIM) { - os << ""; + os << "\"\""; // empty string } else { - os << data.atom; + os << "null"; } - } else { - os << "undefined"; } return os.str(); } diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h index bdb9498..f61cd1d 100644 --- a/src/uscxml/Message.h +++ b/src/uscxml/Message.h @@ -71,8 +71,19 @@ public: }; Data() : type(INTERPRETED) {} + + // TODO: default INTERPRETED is unfortunate Data(const std::string& atom_, Type type_ = INTERPRETED) : atom(atom_), type(type_) {} Data(const char* data, size_t size, const std::string& mimeType, bool adopt = false); + + // convenience constructors + Data(short atom_) : atom(toStr(atom_)), type(INTERPRETED) {} + Data(int atom_) : atom(toStr(atom_)), type(INTERPRETED) {} + Data(unsigned int atom_) : atom(toStr(atom_)), type(INTERPRETED) {} + Data(long atom_) : atom(toStr(atom_)), type(INTERPRETED) {} + Data(unsigned long atom_) : atom(toStr(atom_)), type(INTERPRETED) {} + Data(float atom_) : atom(toStr(atom_)), type(INTERPRETED) {} + Data(double atom_) : atom(toStr(atom_)), type(INTERPRETED) {} Data(bool atom_) : type(INTERPRETED) { if (atom_) { atom = "true"; @@ -80,9 +91,18 @@ public: atom = "false"; } } - template <typename T> Data(T value) : atom(toStr(value)), type(INTERPRETED) {} + template <typename T> Data(T value, Type type_) : atom(toStr(value)), type(type_) {} +#if 0 + // constructor for arbitrary types, skip if type is subclass though (C++11) + // we will have to drop this constructor as it interferes with operator Data() and entails C++11 + template <typename T> + Data(T value, typename std::enable_if<! std::is_base_of<Data, T>::value>::type* = nullptr) + : atom(toStr(value)), type(INTERPRETED) {} +#endif + + explicit Data(const Arabica::DOM::Node<std::string>& dom); virtual ~Data() {} diff --git a/src/uscxml/URL.cpp b/src/uscxml/URL.cpp index 1ed99c3..1ba8404 100644 --- a/src/uscxml/URL.cpp +++ b/src/uscxml/URL.cpp @@ -176,7 +176,7 @@ URLImpl::operator Data() const { data.compound["scheme"] = Data(_uri.scheme(), Data::VERBATIM); data.compound["path"] = Data(_uri.path(), Data::VERBATIM); data.compound["port"] = Data(_uri.port()); - data.compound["isAbsolute"] = Data(_uri.is_absolute() ? "true" : "false"); + data.compound["isAbsolute"] = Data(_uri.is_absolute()); if (_statusCode.length() > 0) data.compound["statusCode"] = Data(_statusCode, Data::VERBATIM); if (_statusMsg.length() > 0) diff --git a/src/uscxml/concurrency/BlockingQueue.h b/src/uscxml/concurrency/BlockingQueue.h index a77bfb7..fc62fce 100644 --- a/src/uscxml/concurrency/BlockingQueue.h +++ b/src/uscxml/concurrency/BlockingQueue.h @@ -57,6 +57,11 @@ public: return ret; } + virtual void clear() { + tthread::lock_guard<tthread::mutex> lock(_mutex); + _queue.clear(); + } + virtual bool isEmpty() { tthread::lock_guard<tthread::mutex> lock(_mutex); return _queue.empty(); diff --git a/src/uscxml/debug/Breakpoint.cpp b/src/uscxml/debug/Breakpoint.cpp new file mode 100644 index 0000000..f64efad --- /dev/null +++ b/src/uscxml/debug/Breakpoint.cpp @@ -0,0 +1,231 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#include "uscxml/debug/Breakpoint.h" +#include "uscxml/Interpreter.h" + +namespace uscxml { + +Breakpoint::Breakpoint(const Data& data) { + subject = UNDEF_SUBJECT; + when = UNDEF_WHEN; + action = UNDEF_ACTION; + + if (data.hasKey("when")) { + if (false) { + } else if (data["when"].atom == "before") { + when = BEFORE; + } else if (data["when"].atom == "after") { + when = AFTER; + } else if (data["when"].atom == "on") { + when = ON; + } + } + + if (data.hasKey("action")) { + if (false) { + } else if (data["action"].atom == "enter") { + action = ENTER; + } else if (data["action"].atom == "exit") { + action = EXIT; + } else if (data["action"].atom == "invoke") { + action = INVOKE; + } else if (data["action"].atom == "cancel") { + action = UNINVOKE; + } + } + + if (data.hasKey("subject")) { + if (false) { + } else if (data["subject"].atom == "state") { + subject = STATE; + } else if (data["subject"].atom == "transition") { + subject = TRANSITION; + } else if (data["subject"].atom == "stable") { + subject = STABLE; + } else if (data["subject"].atom == "microstep") { + subject = MICROSTEP; + } else if (data["subject"].atom == "event") { + subject = EVENT; + } else if (data["subject"].atom == "invoker") { + subject = INVOKER; + } else if (data["subject"].atom == "exec") { + subject = EXECUTABLE; + } + } + + if (data.hasKey("condition")) + condition = data["condition"].atom; + + if (data.hasKey("invokeId")) + invokeId = data["invokeId"].atom; + + if (data.hasKey("invokeType")) + invokeType = data["invokeType"].atom; + + if (data.hasKey("eventName")) + eventName = data["eventName"].atom; + + if (data.hasKey("stateId")) + stateId = data["stateId"].atom; + + if (data.hasKey("transSource")) + transSource = data["transSource"].atom; + + if (data.hasKey("transTarget")) + transTarget = data["transTarget"].atom; + +} + +Data Breakpoint::toData() const { + Data data; + + switch (subject) { + case STATE: + data.compound["subject"] = Data("state", Data::VERBATIM); + break; + case TRANSITION: + data.compound["subject"] = Data("transition", Data::VERBATIM); + break; + case STABLE: + data.compound["subject"] = Data("stable", Data::VERBATIM); + break; + case MICROSTEP: + data.compound["subject"] = Data("microstep", Data::VERBATIM); + break; + case EVENT: + data.compound["subject"] = Data("event", Data::VERBATIM); + break; + case INVOKER: + data.compound["subject"] = Data("invoker", Data::VERBATIM); + break; + case EXECUTABLE: + data.compound["subject"] = Data("exec", Data::VERBATIM); + break; + default: + break; + } + + switch (when) { + case AFTER: + data.compound["when"] = Data("after", Data::VERBATIM); + break; + case BEFORE: + data.compound["when"] = Data("before", Data::VERBATIM); + break; + case ON: + data.compound["when"] = Data("on", Data::VERBATIM); + break; + default: + break; + } + + switch (action) { + case ENTER: + data.compound["action"] = Data("enter", Data::VERBATIM); + break; + case EXIT: + data.compound["action"] = Data("exit", Data::VERBATIM); + break; + case INVOKE: + data.compound["action"] = Data("invoke", Data::VERBATIM); + break; + case UNINVOKE: + data.compound["action"] = Data("cancel", Data::VERBATIM); + break; + default: + break; + } + + if (invokeId.length() > 0) + data.compound["invokeId"] = Data(invokeId, Data::VERBATIM); + + if (invokeType.length() > 0) + data.compound["invokeType"] = Data(invokeType, Data::VERBATIM); + + if (eventName.length() > 0) + data.compound["eventName"] = Data(eventName, Data::VERBATIM); + + if (stateId.length() > 0) + data.compound["stateId"] = Data(stateId, Data::VERBATIM); + + if (transSource.length() > 0) + data.compound["transSource"] = Data(transSource, Data::VERBATIM); + + if (transTarget.length() > 0) + data.compound["transTarget"] = Data(transTarget, Data::VERBATIM); + + if (condition.length() > 0) + data.compound["condition"] = Data(condition, Data::VERBATIM); + + return data; +} + +bool Breakpoint::matches(const Breakpoint& other) const { + // would we match the given breakpoint? + + if (subject != UNDEF_SUBJECT && + other.subject != UNDEF_SUBJECT && + other.subject != subject) + return false; // subject does not match + + if (when != UNDEF_WHEN && + other.when != UNDEF_WHEN && + other.when != when) + return false; // time does not match + + if (action != UNDEF_ACTION && + other.action != UNDEF_ACTION && + other.action != action) + return false; // action does not match + + // when we have a qualifier it has to match + if(invokeId.length() > 0 && !InterpreterImpl::nameMatch(invokeId, other.invokeId)) { + return false; + } + + if(invokeType.length() > 0 && !InterpreterImpl::nameMatch(invokeType, other.invokeType)) { + return false; + } + + if(stateId.length() > 0 && !InterpreterImpl::nameMatch(stateId, other.stateId)) { + return false; + } + + if(eventName.length() > 0 && !InterpreterImpl::nameMatch(eventName, other.eventName)) { + return false; + } + + if(transSource.length() > 0 && !InterpreterImpl::nameMatch(transSource, other.transSource)) { + return false; + } + + if(transTarget.length() > 0 && !InterpreterImpl::nameMatch(transTarget, other.transTarget)) { + return false; + } + + return true; +} + + +bool Breakpoint::isValid() { + return true; +} + +}
\ No newline at end of file diff --git a/src/uscxml/debug/Breakpoint.h b/src/uscxml/debug/Breakpoint.h new file mode 100644 index 0000000..b2861d8 --- /dev/null +++ b/src/uscxml/debug/Breakpoint.h @@ -0,0 +1,77 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#ifndef BREAKPOINT_H_VR7K7T1X +#define BREAKPOINT_H_VR7K7T1X + +#include "uscxml/Message.h" + +namespace uscxml { + +class USCXML_API Breakpoint { +public: + + enum When { + UNDEF_WHEN, AFTER, BEFORE, ON + }; + + enum Subject { + UNDEF_SUBJECT, STATE, TRANSITION, STABLE, MICROSTEP, EVENT, INVOKER, EXECUTABLE + }; + + enum Action { + UNDEF_ACTION, ENTER, EXIT, INVOKE, UNINVOKE + }; + + Breakpoint() {} + Breakpoint(const Data& data); + + // would we match the given breakpoint as well? + bool matches(const Breakpoint& other) const; + + bool isValid(); + + Data toData() const; + + bool operator<(const Breakpoint& other) const { + return (origData < other.origData); + } + + When when; + Subject subject; + Action action; + + std::string invokeId; + std::string invokeType; + + std::string eventName; + + std::string stateId; + std::string transSource; + std::string transTarget; + + std::string condition; + Data origData; +}; + +} + + + +#endif /* end of include guard: BREAKPOINT_H_VR7K7T1X */ diff --git a/src/uscxml/debug/Debugger.cpp b/src/uscxml/debug/Debugger.cpp new file mode 100644 index 0000000..aa97a22 --- /dev/null +++ b/src/uscxml/debug/Debugger.cpp @@ -0,0 +1,247 @@ +/** +* @file +* @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) +* @copyright Simplified BSD +* +* @cond +* This program is free software: you can redistribute it and/or modify +* it under the terms of the FreeBSD license as published by the FreeBSD +* project. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* +* You should have received a copy of the FreeBSD license along with this +* program. If not, see <http://www.opensource.org/licenses/bsd-license>. +* @endcond +*/ + +#include "uscxml/debug/Debugger.h" +#include "uscxml/DOMUtils.h" + +namespace uscxml { + +void Debugger::afterCompletion(Interpreter interpreter) { + Data msg; + msg.compound["replyType"] = Data("finished", Data::VERBATIM); + pushData(msg); +} + +std::list<Breakpoint> getQualifiedStateBreakpoints(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, Breakpoint breakpointTemplate) { + std::list<Breakpoint> breakpoints; + + Breakpoint bp = breakpointTemplate; // copy base as template + bp.stateId = ATTR(state, "id"); + bp.subject = Breakpoint::STATE; + breakpoints.push_back(bp); + + return breakpoints; +} + +std::list<Breakpoint> getQualifiedInvokeBreakpoints(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string invokeId, Breakpoint breakpointTemplate) { + std::list<Breakpoint> breakpoints; + + Breakpoint bp = breakpointTemplate; // copy base as template + bp.subject = Breakpoint::INVOKER; + bp.invokeId = invokeId; + + if (HAS_ATTR(invokeElem, "type")) { + bp.invokeType = ATTR(invokeElem, "type"); + } else if (HAS_ATTR(invokeElem, "typeexpr")) { + bp.invokeType = interpreter.getDataModel().evalAsString(ATTR(invokeElem, "typeexpr")); + } + + breakpoints.push_back(bp); + + return breakpoints; +} + +std::list<Breakpoint> getQualifiedTransBreakpoints(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, Breakpoint breakpointTemplate) { + std::list<Breakpoint> breakpoints; + + Arabica::DOM::Element<std::string> source(interpreter.getSourceState(transition)); + Arabica::XPath::NodeSet<std::string> targets = interpreter.getTargetStates(transition); + + for (int j = 0; j < targets.size(); j++) { + Arabica::DOM::Element<std::string> target(targets[j]); + + Breakpoint bp = breakpointTemplate; // copy base as template + bp.transSource = ATTR(source, "id"); + bp.transTarget = ATTR(target, "id"); + bp.subject = Breakpoint::TRANSITION; + + breakpoints.push_back(bp); + } + + return breakpoints; +} + +void Debugger::beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition) { + handleTransition(interpreter, transition, Breakpoint::BEFORE); +} +void Debugger::afterTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition) { + handleTransition(interpreter, transition, Breakpoint::AFTER); +} +void Debugger::beforeExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) { + handleState(interpreter, state, Breakpoint::BEFORE, Breakpoint::EXIT); +} +void Debugger::afterExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) { + handleState(interpreter, state, Breakpoint::AFTER, Breakpoint::EXIT); +} +void Debugger::beforeEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) { + handleState(interpreter, state, Breakpoint::BEFORE, Breakpoint::ENTER); +} +void Debugger::afterEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state) { + handleState(interpreter, state, Breakpoint::AFTER, Breakpoint::ENTER); +} +void Debugger::beforeUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) { + handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::BEFORE, Breakpoint::UNINVOKE); +} +void Debugger::afterUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) { + handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::AFTER, Breakpoint::UNINVOKE); +} +void Debugger::beforeInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) { + handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::BEFORE, Breakpoint::INVOKE); +} +void Debugger::afterInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid) { + handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::AFTER, Breakpoint::INVOKE); +} +void Debugger::onStableConfiguration(Interpreter interpreter) { + handleStable(interpreter, Breakpoint::ON); +} +void Debugger::beforeMicroStep(Interpreter interpreter) { + handleMicrostep(interpreter, Breakpoint::BEFORE); +} +void Debugger::afterMicroStep(Interpreter interpreter) { + handleMicrostep(interpreter, Breakpoint::AFTER); +} +void Debugger::beforeProcessingEvent(Interpreter interpreter, const Event& event) { + handleEvent(interpreter, event, Breakpoint::BEFORE); +} + +void Debugger::handleEvent(Interpreter interpreter, const Event& event, Breakpoint::When when) { + if (!interpreter.isRunning()) + return; + + std::list<Breakpoint> breakpoints; + + Breakpoint breakpoint; + breakpoint.when = when; + breakpoint.eventName = event.name; + breakpoint.subject = Breakpoint::EVENT; + breakpoints.push_back(breakpoint); + + checkBreakpoints(interpreter, breakpoints); + +} + +void Debugger::handleStable(Interpreter interpreter, Breakpoint::When when) { + if (!interpreter.isRunning()) + return; + + std::list<Breakpoint> breakpoints; + + Breakpoint breakpoint; + breakpoint.when = when; + breakpoint.subject = Breakpoint::STABLE; + breakpoints.push_back(breakpoint); + + checkBreakpoints(interpreter, breakpoints); +} + +void Debugger::handleMicrostep(Interpreter interpreter, Breakpoint::When when) { + if (!interpreter.isRunning()) + return; + + std::list<Breakpoint> breakpoints; + + Breakpoint breakpoint; + breakpoint.when = when; + breakpoint.subject = Breakpoint::MICROSTEP; + breakpoints.push_back(breakpoint); + + checkBreakpoints(interpreter, breakpoints); +} + +void Debugger::handleTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition, Breakpoint::When when) { + if (!interpreter.isRunning()) + return; + + Breakpoint breakpointTemplate; + breakpointTemplate.when = when; + std::list<Breakpoint> qualifiedBreakpoints = getQualifiedTransBreakpoints(interpreter, transition, breakpointTemplate); + checkBreakpoints(interpreter, qualifiedBreakpoints); +} + +void Debugger::handleState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state, Breakpoint::When when, Breakpoint::Action action) { + if (!interpreter.isRunning()) + return; + + Breakpoint breakpointTemplate; + breakpointTemplate.when = when; + breakpointTemplate.action = action; + std::list<Breakpoint> qualifiedBreakpoints = getQualifiedStateBreakpoints(interpreter, state, breakpointTemplate); + checkBreakpoints(interpreter, qualifiedBreakpoints); + +} + +void Debugger::handleInvoke(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeId, Breakpoint::When when, Breakpoint::Action action) { + if (!interpreter.isRunning()) + return; + + Breakpoint breakpointTemplate; + breakpointTemplate.when = when; + breakpointTemplate.action = action; + std::list<Breakpoint> qualifiedBreakpoints = getQualifiedInvokeBreakpoints(interpreter, invokeElem, invokeId, breakpointTemplate); + checkBreakpoints(interpreter, qualifiedBreakpoints); + +} + +void Debugger::checkBreakpoints(Interpreter interpreter, const std::list<Breakpoint> qualifiedBreakpoints) { + std::list<Breakpoint>::const_iterator qualifiedBreakpointIter = qualifiedBreakpoints.begin(); + while(qualifiedBreakpointIter != qualifiedBreakpoints.end()) { + const Breakpoint& qualifiedBreakpoint = *qualifiedBreakpointIter++; + + // check if one of the user-supplied breakpoints match + bool userBreakpointMatched = false; + std::set<Breakpoint>::const_iterator breakpointIter = _breakPoints.begin(); + while(breakpointIter != _breakPoints.end()) { + const Breakpoint& breakpoint = *breakpointIter++; + if (breakpoint.matches(qualifiedBreakpoint)) { + Data replyData; + replyData.compound["breakpoint"] = breakpoint.toData(); + replyData.compound["qualified"] = qualifiedBreakpoint.toData(); + + userBreakpointMatched = true; + hitBreakpoint(interpreter, replyData); + } + } + if (_isStepping && !userBreakpointMatched) { + Data replyData; + replyData.compound["qualified"] = qualifiedBreakpoint.toData(); + hitBreakpoint(interpreter, replyData); + } + } +} + +void Debugger::send(google::LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len) { + + // _sendQueue is thread-safe, not sure about ToString though + + LogMessage msg(severity, + full_filename, + base_filename, + line, + tm_time, + std::string(message, message_len), + ToString(severity, base_filename, line, tm_time, message, message_len)); + msg.compound["replyType"] = Data("log", Data::VERBATIM); + pushData(msg); +} + + +}
\ No newline at end of file diff --git a/src/uscxml/debug/Debugger.h b/src/uscxml/debug/Debugger.h new file mode 100644 index 0000000..dfc197d --- /dev/null +++ b/src/uscxml/debug/Debugger.h @@ -0,0 +1,115 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#ifndef DEBUGGERMONITOR_H_Z050WPFH +#define DEBUGGERMONITOR_H_Z050WPFH + +#include "uscxml/Message.h" +#include "uscxml/Interpreter.h" +#include "uscxml/debug/Breakpoint.h" + +#include <glog/logging.h> + +namespace uscxml { + +class USCXML_API Debugger : public InterpreterMonitor, public google::LogSink { +public: + Debugger() { + _isStepping = false; + } + virtual ~Debugger() {} + + class LogMessage : public Data { + public: + LogMessage(google::LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct ::tm* tm_time, + std::string message, std::string formatted) { + + compound["severity"] = severity; + compound["fullFilename"] = Data(full_filename, Data::VERBATIM); + compound["baseFilename"] = Data(base_filename, Data::VERBATIM); + compound["line"] = line; + compound["message"] = Data(message, Data::VERBATIM); + compound["time"] = Data(mktime((struct ::tm*)tm_time), Data::INTERPRETED); + compound["formatted"] = Data(formatted, Data::VERBATIM); + } + }; + + virtual void pushData(Data pushData) = 0; + virtual void hitBreakpoint(const Interpreter& interpreter, Data data) = 0; + + void stepping(bool enable) { + _isStepping = enable; + } + + // InterpreterMonitor + virtual void beforeProcessingEvent(Interpreter interpreter, const Event& event); + virtual void beforeMicroStep(Interpreter interpreter); + virtual void beforeExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state); + virtual void afterExitingState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state); + virtual void beforeUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid); + virtual void afterUninvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid); + virtual void beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition); + virtual void afterTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition); + virtual void beforeEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state); + virtual void afterEnteringState(Interpreter interpreter, const Arabica::DOM::Element<std::string>& state); + virtual void beforeInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid); + virtual void afterInvoking(Interpreter interpreter, const Arabica::DOM::Element<std::string>& invokeElem, const std::string& invokeid); + virtual void afterMicroStep(Interpreter interpreter); + virtual void beforeExecutingContent(Interpreter interpreter, const Arabica::DOM::Element<std::string>& element) {} + virtual void afterExecutingContent(Interpreter interpreter, const Arabica::DOM::Element<std::string>& element) {} + virtual void onStableConfiguration(Interpreter interpreter); + virtual void beforeCompletion(Interpreter interpreter) {} + virtual void afterCompletion(Interpreter interpreter); + + // Logsink + virtual void send(google::LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len); + +protected: + + void handleTransition(Interpreter interpreter, + const Arabica::DOM::Element<std::string>& transition, + Breakpoint::When when); + void handleState(Interpreter interpreter, + const Arabica::DOM::Element<std::string>& state, + Breakpoint::When when, + Breakpoint::Action action); + void handleInvoke(Interpreter interpreter, + const Arabica::DOM::Element<std::string>& invokeElem, + const std::string& invokeId, + Breakpoint::When when, + Breakpoint::Action action); + void handleStable(Interpreter interpreter, Breakpoint::When when); + void handleMicrostep(Interpreter interpreter, Breakpoint::When when); + void handleEvent(Interpreter interpreter, const Event& event, Breakpoint::When when); + void checkBreakpoints(Interpreter interpreter, const std::list<Breakpoint> qualifiedBreakpoints); + + bool _isStepping; + std::set<Breakpoint> _breakPoints; + +}; + +} + + +#endif /* end of include guard: DEBUGGERMONITOR_H_Z050WPFH */ diff --git a/src/uscxml/debug/DebuggerServlet.cpp b/src/uscxml/debug/DebuggerServlet.cpp new file mode 100644 index 0000000..e179b8c --- /dev/null +++ b/src/uscxml/debug/DebuggerServlet.cpp @@ -0,0 +1,321 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#include "uscxml/debug/DebuggerServlet.h" +#include "uscxml/UUID.h" +#include <boost/algorithm/string.hpp> + +namespace uscxml { + +void DebuggerServlet::pushData(Data pushData) { + std::cout << "trying to push " << pushData["replyType"].atom << std::endl; + _sendQueue.push(pushData); + serverPushData(); +} + +void DebuggerServlet::serverPushData() { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + if (_sendQueue.isEmpty()) + return; + + if (!_clientConn) + return; + + Data reply = _sendQueue.pop(); + std::cout << "pushing " << reply["replyType"].atom << std::endl; + returnData(_clientConn, reply); + _clientConn = HTTPServer::Request(); +} + +void DebuggerServlet::returnData(const HTTPServer::Request& request, Data replyData) { + HTTPServer::Reply reply(request); + + if (!replyData.hasKey("status")) { + replyData.compound["status"] = Data("success", Data::VERBATIM); + } + + reply.content = Data::toJSON(replyData); + reply.headers["Access-Control-Allow-Origin"] = "*"; + reply.headers["Content-Type"] = "application/json"; + HTTPServer::reply(reply); +} + +void DebuggerServlet::hitBreakpoint(const Interpreter& interpreter, + Data data) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + data.compound["replyType"] = Data("breakpoint", Data::VERBATIM); + pushData(data); + + _resumeCond.wait(_mutex); + tthread::this_thread::sleep_for(tthread::chrono::milliseconds(200)); +} + +bool DebuggerServlet::isCORS(const HTTPServer::Request& request) { + return (request.data["type"].atom == "options" && + request.data["header"].hasKey("Origin") && + request.data["header"].hasKey("Access-Control-Request-Method")); +} + +void DebuggerServlet::handleCORS(const HTTPServer::Request& request) { + HTTPServer::Reply corsReply(request); + if (request.data["header"].hasKey("Origin")) { + corsReply.headers["Access-Control-Allow-Origin"] = request.data["header"]["Origin"].atom; + } else { + corsReply.headers["Access-Control-Allow-Origin"] = "*"; + } + if (request.data["header"].hasKey("Access-Control-Request-Method")) + corsReply.headers["Access-Control-Allow-Methods"] = request.data["header"]["Access-Control-Request-Method"].atom; + if (request.data["header"].hasKey("Access-Control-Request-Headers")) + corsReply.headers["Access-Control-Allow-Headers"] = request.data["header"]["Access-Control-Request-Headers"].atom; + + // std::cout << "CORS!" << std::endl << request << std::endl; + HTTPServer::reply(corsReply); +} + +bool DebuggerServlet::httpRecvRequest(const HTTPServer::Request& request) { + if (!request.data.hasKey("path")) + return false; // returnError(request); + + if (isCORS(request)) { + handleCORS(request); + return true; + } + + std::cout << request.data["path"] << ": " << request.data["content"] << std::endl; + + if (false) { + } else if (boost::starts_with(request.data["path"].atom, "/poll")) { + processPoll(request); + } else if (boost::starts_with(request.data["path"].atom, "/connect")) { + processConnect(request); + } else if (boost::starts_with(request.data["path"].atom, "/disconnect")) { + processDisconnect(request); + } else if (boost::starts_with(request.data["path"].atom, "/sessions")) { + processListSessions(request); + } else if (boost::starts_with(request.data["path"].atom, "/breakpoint/add")) { + processAddBreakPoint(request); + } else if (boost::starts_with(request.data["path"].atom, "/breakpoint/remove")) { + processRemoveBreakPoint(request); + } else if (boost::starts_with(request.data["path"].atom, "/debug/prepare")) { + processDebugPrepare(request); + } else if (boost::starts_with(request.data["path"].atom, "/debug/start")) { + processDebugStart(request); + } else if (boost::starts_with(request.data["path"].atom, "/debug/stop")) { + processDebugStop(request); + } else if (boost::starts_with(request.data["path"].atom, "/debug/step")) { + processDebugStep(request); + } else if (boost::starts_with(request.data["path"].atom, "/debug/pause")) { + processDebugPause(request); + } else if (boost::starts_with(request.data["path"].atom, "/debug/resume")) { + processDebugResume(request); + } else if (boost::starts_with(request.data["path"].atom, "/debug/eval")) { + processDebugEval(request); + } + + return true; +} + +void DebuggerServlet::processPoll(const HTTPServer::Request& request) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + _clientConn = request; + serverPushData(); +} + +void DebuggerServlet::processDebugPrepare(const HTTPServer::Request& request) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + +// std::cout << "clearing all pushes" << std::endl; +// _sendQueue.clear(); + + // this will call the destructor if _interpreter already is set + _resumeCond.notify_all(); + _interpreter = Interpreter::fromXML(request.data["content"].atom); + + Data replyData; + if (_interpreter) { + // register ourself as a monitor + _interpreter.addMonitor(this); + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + returnData(request, replyData); +} + +void DebuggerServlet::processDebugStart(const HTTPServer::Request& request) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + Data replyData; + if (_interpreter) { + // register ourself as a monitor + _interpreter.start(); + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + returnData(request, replyData); +} + +void DebuggerServlet::processDebugStop(const HTTPServer::Request& request) { +// tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + stepping(false); + + Data replyData; + if (_interpreter) { + _interpreter.stop(); + _resumeCond.notify_all(); // unblock breakpoints + _interpreter.join(); + _interpreter = Interpreter(); // empty interpreter, calls destructor + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("Interpreter already stopped", Data::VERBATIM); + } + returnData(request, replyData); +} + +void DebuggerServlet::processDebugEval(const HTTPServer::Request& request) { + Data replyData; + if (!_interpreter) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No interpreter running", Data::VERBATIM); + } else if (!_interpreter.getDataModel()) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No datamodel available", Data::VERBATIM); + } else if (!request.data["content"].hasKey("expression")) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No expression given", Data::VERBATIM); + } else { + std::string expr = request.data["content"]["expression"].atom; + try { + replyData.compound["eval"] = _interpreter.getDataModel().getStringAsData(expr); + } catch (Event e) { + replyData.compound["eval"] = e.data; + replyData.compound["eval"].compound["error"] = Data(e.name, Data::VERBATIM); + } + replyData.compound["status"] = Data("success", Data::VERBATIM); + } + returnData(request, replyData); +} + +void DebuggerServlet::processDebugStep(const HTTPServer::Request& request) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + stepping(true); + _resumeCond.notify_one(); + + Data replyData; + if (_interpreter && !_interpreter.isRunning()) { + // register ourself as a monitor + _interpreter.start(); + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + returnData(request, replyData); + +} + +void DebuggerServlet::processDebugResume(const HTTPServer::Request& request) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + stepping(false); + + Data replyData; + replyData.compound["status"] = Data("success", Data::VERBATIM); + returnData(request, replyData); + + _resumeCond.notify_one(); +} + +void DebuggerServlet::processDebugPause(const HTTPServer::Request& request) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + + Data replyData; + replyData.compound["status"] = Data("success", Data::VERBATIM); + returnData(request, replyData); +} + +void DebuggerServlet::processConnect(const HTTPServer::Request& request) { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + _sessionId = UUID::getUUID(); + _breakPoints.clear(); +// _sendQueue.clear(); + + Data replyData; + replyData.compound["session"] = Data(_sessionId, Data::VERBATIM); + replyData.compound["status"] = Data("success", Data::VERBATIM); + returnData(request, replyData); +} + +void DebuggerServlet::processListSessions(const HTTPServer::Request& request) { + Data replyData; + + // TODO: return actual data + Data sessionData; + sessionData.compound["name"] = Data("Not actually a Session", Data::VERBATIM); + sessionData.compound["id"] = Data("23452523-wg23g2g2-234t2g-23g2g", Data::VERBATIM); + replyData.compound["sessions"].array.push_back(sessionData); + + sessionData.compound["name"] = Data("But returned from the server!", Data::VERBATIM); + sessionData.compound["id"] = Data("swfgsgfw-g232vqvq-234t2g-23g2g", Data::VERBATIM); + replyData.compound["sessions"].array.push_back(sessionData); + + replyData.compound["status"] = Data("success", Data::VERBATIM); + returnData(request, replyData); +} + +void DebuggerServlet::processDisconnect(const HTTPServer::Request& request) { + Data replyData; + replyData.compound["status"] = Data("success", Data::VERBATIM); + returnData(request, replyData); +} + +void DebuggerServlet::processAddBreakPoint(const HTTPServer::Request& request) { + Breakpoint breakPoint(request.data["content"]); + Data replyData; + if (breakPoint.isValid()) { + replyData.compound["status"] = Data("success", Data::VERBATIM); + + if (_breakPoints.find(breakPoint) == _breakPoints.end()) { + _breakPoints.insert(breakPoint); + } + } else { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + returnData(request, replyData); +} + +void DebuggerServlet::processRemoveBreakPoint(const HTTPServer::Request& request) { + Breakpoint breakPoint(request.data["content"]); + Data replyData; + if (_breakPoints.erase(breakPoint) > 0) { + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["message"] = Data("No such breakpoint", Data::VERBATIM); + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + returnData(request, replyData); +} + + +}
\ No newline at end of file diff --git a/src/uscxml/debug/DebuggerServlet.h b/src/uscxml/debug/DebuggerServlet.h new file mode 100644 index 0000000..5cd0be9 --- /dev/null +++ b/src/uscxml/debug/DebuggerServlet.h @@ -0,0 +1,83 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#ifndef DEBUGGERSERVLET_H_ATUMDA3G +#define DEBUGGERSERVLET_H_ATUMDA3G + +#include "uscxml/Common.h" +#include "getopt.h" + +#include "uscxml/server/HTTPServer.h" +#include "uscxml/Interpreter.h" + +#include "uscxml/debug/Debugger.h" +#include "uscxml/concurrency/tinythread.h" + +namespace uscxml { + +class USCXML_API DebuggerServlet : public Debugger, public HTTPServlet { +public: + virtual ~DebuggerServlet() {} + + // from Debugger + virtual void addBreakpoint(const Breakpoint& breakpoint) {}; + + bool isCORS(const HTTPServer::Request& request); + void handleCORS(const HTTPServer::Request& request); + + bool httpRecvRequest(const HTTPServer::Request& request); + void setURL(const std::string& url) { + _url = url; + } + + void pushData(Data pushData); + void returnData(const HTTPServer::Request& request, Data replyData); + + void hitBreakpoint(const Interpreter& interpreter, + Data data); + + void processDebugEval(const HTTPServer::Request& request); + void processDebugPrepare(const HTTPServer::Request& request); + void processDebugStart(const HTTPServer::Request& request); + void processDebugStop(const HTTPServer::Request& request); + void processDebugStep(const HTTPServer::Request& request); + void processDebugResume(const HTTPServer::Request& request); + void processDebugPause(const HTTPServer::Request& request); + void processConnect(const HTTPServer::Request& request); + void processListSessions(const HTTPServer::Request& request); + void processDisconnect(const HTTPServer::Request& request); + void processAddBreakPoint(const HTTPServer::Request& request); + void processRemoveBreakPoint(const HTTPServer::Request& request); + void processPoll(const HTTPServer::Request& request); + +protected: + void serverPushData(); + + Interpreter _interpreter; + std::string _sessionId; + std::string _url; + HTTPServer::Request _clientConn; + tthread::condition_variable _resumeCond; + tthread::recursive_mutex _mutex; + concurrency::BlockingQueue<Data> _sendQueue; +}; + +} + +#endif /* end of include guard: DEBUGGERSERVLET_H_ATUMDA3G */ diff --git a/src/uscxml/debug/SCXMLDotWriter.cpp b/src/uscxml/debug/SCXMLDotWriter.cpp index 07e7b9a..2058d72 100644 --- a/src/uscxml/debug/SCXMLDotWriter.cpp +++ b/src/uscxml/debug/SCXMLDotWriter.cpp @@ -32,9 +32,9 @@ SCXMLDotWriter::SCXMLDotWriter() { _indentation = 0; } -SCXMLDotWriter::SCXMLDotWriter(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& transitions) { +SCXMLDotWriter::SCXMLDotWriter(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition) { _interpreter = interpreter; - _transitions = transitions; + _transition = transition; _iteration = 0; _indentation = 0; } @@ -61,10 +61,10 @@ void SCXMLDotWriter::beforeMicroStep(Interpreter interpreter) { // toDot(fileSS.str(), interpreter); } -void SCXMLDotWriter::beforeTakingTransitions(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& transitions) { +void SCXMLDotWriter::beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition) { std::ostringstream fileSS; fileSS << interpreter.getName() << "." << std::setw(6) << std::setfill('0') << _iteration++ << ".dot"; - toDot(fileSS.str(), interpreter, transitions); + toDot(fileSS.str(), interpreter, transition); } std::string SCXMLDotWriter::getPrefix() { @@ -74,10 +74,10 @@ std::string SCXMLDotWriter::getPrefix() { return prefix; } -void SCXMLDotWriter::toDot(const std::string& filename, Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& transitions) { +void SCXMLDotWriter::toDot(const std::string& filename, Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition) { std::ofstream outfile(filename.c_str()); NodeList<std::string > scxmlElems = interpreter.getDocument().getElementsByTagName("scxml"); - SCXMLDotWriter writer(interpreter, transitions); + SCXMLDotWriter writer(interpreter, transition); if (scxmlElems.getLength() > 0) { writer._indentation++; outfile << "digraph {" << std::endl; @@ -168,7 +168,7 @@ void SCXMLDotWriter::writeStateElement(std::ostream& os, const Arabica::DOM::Ele for (int i = 0; i < childElems.getLength(); i++) { if (childElems.item(i).getNodeType() == Node_base::ELEMENT_NODE && iequals(TAGNAME(childElems.item(i)), "transition")) { writeTransitionElement(os, (Arabica::DOM::Element<std::string>)childElems.item(i)); - bool active = Interpreter::isMember(childElems.item(i), _transitions); + bool active = (childElems.item(i) == _transition); os << getPrefix() << "\"" << elemId << "\" -> \"" << idForNode(childElems.item(i)) << "\" [arrowhead=none" << std::endl; if (active) { os << ", penwidth=3, color=red]" << std::endl; @@ -202,7 +202,7 @@ void SCXMLDotWriter::writeTransitionElement(std::ostream& os, const Arabica::DOM Arabica::XPath::NodeSet<std::string> targetStates = _interpreter.getTargetStates(elem); - bool active = Interpreter::isMember(elem, _transitions); + bool active = (elem == _transition); std::string label; os << getPrefix() << "\"" << elemId << "\"["; diff --git a/src/uscxml/debug/SCXMLDotWriter.h b/src/uscxml/debug/SCXMLDotWriter.h index 2d3625c..4e2d7a8 100644 --- a/src/uscxml/debug/SCXMLDotWriter.h +++ b/src/uscxml/debug/SCXMLDotWriter.h @@ -65,12 +65,12 @@ public: virtual void onStableConfiguration(Interpreter interpreter); virtual void afterCompletion(Interpreter interpreter); - virtual void beforeTakingTransitions(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& transitions); + virtual void beforeTakingTransition(Interpreter interpreter, const Arabica::DOM::Element<std::string>& transition); virtual void beforeMicroStep(Interpreter interpreter); static void toDot(const std::string& filename, Interpreter interpreter, - const Arabica::XPath::NodeSet<std::string>& transitions = Arabica::XPath::NodeSet<std::string>()); + const Arabica::DOM::Element<std::string>& transition = Arabica::DOM::Element<std::string>()); std::string getDetailedLabel(const Arabica::DOM::Element<std::string>& elem, int indentation = 0); std::string colorForIndent(int indent); @@ -84,7 +84,7 @@ public: protected: SCXMLDotWriter(Interpreter interpreter, - const Arabica::XPath::NodeSet<std::string>& transitions); + const Arabica::DOM::Element<std::string>& transition); void writeSCXMLElement(std::ostream& os, const Arabica::DOM::Element<std::string>& elem); void writeStateElement(std::ostream& os, const Arabica::DOM::Element<std::string>& elem); @@ -95,7 +95,7 @@ protected: int _indentation; // these are only set in ephemeral instances per monitor call - Arabica::XPath::NodeSet<std::string> _transitions; + Arabica::DOM::Element<std::string> _transition; Interpreter _interpreter; }; diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp index 772ad96..9e9204a 100644 --- a/src/uscxml/interpreter/InterpreterDraft6.cpp +++ b/src/uscxml/interpreter/InterpreterDraft6.cpp @@ -30,144 +30,130 @@ using namespace Arabica::DOM; // see: http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation void InterpreterDraft6::interpret() { - tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); - if (!_isInitialized) - init(); + try { + tthread::lock_guard<tthread::recursive_mutex> lock(_mutex); + if (!_isInitialized) + init(); -// std::cout << _scxml << std::endl; - - if (!_scxml) { -// _mutex.unlock(); - return; - } -// dump(); - - // just make sure we have a session id - assert(_sessionId.length() > 0); - - setupIOProcessors(); - - std::string datamodelName; - if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "datamodel")) - datamodelName = ATTR(_scxml, "datamodel"); - if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "profile")) // SCION SCXML uses profile to specify datamodel - datamodelName = ATTR(_scxml, "profile"); - if(datamodelName.length() > 0) { - _dataModel = _factory->createDataModel(datamodelName, this); - if (!_dataModel) { - Event e; - e.data.compound["cause"] = Data("Cannot instantiate datamodel"); - throw e; + if (!_scxml) { + return; } - } else { - _dataModel = _factory->createDataModel("null", this); - } - if(datamodelName.length() > 0 && !_dataModel) { - LOG(ERROR) << "No datamodel for " << datamodelName << " registered"; - } - - if (_dataModel) { - _dataModel.assign("_x.args", _cmdLineOptions); - } - - _running = true; - _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); - - // @TODO: Reread http://www.w3.org/TR/scxml/#DataBinding - - if (_dataModel && _binding == EARLY) { - // initialize all data elements - NodeSet<std::string> dataElems = _xpath.evaluate("//" + _xpathPrefix + "data", _scxml).asNodeSet(); - for (unsigned int i = 0; i < dataElems.size(); i++) { - // do not process data elements of nested documents from invokers - if (!getAncestorElement(dataElems[i], _xmlNSPrefix + "invoke")) - if (dataElems[i].getNodeType() == Node_base::ELEMENT_NODE) { - initializeData(Element<std::string>(dataElems[i])); - } + // dump(); + + // just make sure we have a session id + assert(_sessionId.length() > 0); + + setupIOProcessors(); + + std::string datamodelName; + if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "datamodel")) + datamodelName = ATTR(_scxml, "datamodel"); + if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "profile")) // SCION SCXML uses profile to specify datamodel + datamodelName = ATTR(_scxml, "profile"); + if(datamodelName.length() > 0) { + _dataModel = _factory->createDataModel(datamodelName, this); + if (!_dataModel) { + Event e; + e.data.compound["cause"] = Data("Cannot instantiate datamodel"); + throw e; + } + } else { + _dataModel = _factory->createDataModel("null", this); } - } else if(_dataModel) { - // initialize current data elements - NodeSet<std::string> topDataElems = filterChildElements(_xmlNSPrefix + "data", filterChildElements(_xmlNSPrefix + "datamodel", _scxml)); - for (unsigned int i = 0; i < topDataElems.size(); i++) { - if (topDataElems[i].getNodeType() == Node_base::ELEMENT_NODE) - initializeData(Element<std::string>(topDataElems[i])); + if(datamodelName.length() > 0 && !_dataModel) { + LOG(ERROR) << "No datamodel for " << datamodelName << " registered"; } - } - // executeGlobalScriptElements - NodeSet<std::string> globalScriptElems = filterChildElements(_xmlNSPrefix + "script", _scxml); - for (unsigned int i = 0; i < globalScriptElems.size(); i++) { if (_dataModel) { - executeContent(globalScriptElems[i]); + _dataModel.assign("_x.args", _cmdLineOptions); } - } - NodeSet<std::string> initialTransitions; + _running = true; + _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); - if (_userDefinedStartConfiguration.size() > 0) { - // we emulate entering a given configuration by creating a pseudo deep history - Element<std::string> initHistory = _document.createElementNS(_nsURL, "history"); - initHistory.setAttribute("id", UUID::getUUID()); - initHistory.setAttribute("type", "deep"); - _scxml.insertBefore(initHistory, _scxml.getFirstChild()); + // @TODO: Reread http://www.w3.org/TR/scxml/#DataBinding - std::string histId = ATTR(initHistory, "id"); - NodeSet<std::string> histStates; - for (int i = 0; i < _userDefinedStartConfiguration.size(); i++) { - histStates.push_back(getState(_userDefinedStartConfiguration[i])); + if (_dataModel && _binding == EARLY) { + // initialize all data elements + NodeSet<std::string> dataElems = _xpath.evaluate("//" + _xpathPrefix + "data", _scxml).asNodeSet(); + for (unsigned int i = 0; i < dataElems.size(); i++) { + // do not process data elements of nested documents from invokers + if (!getAncestorElement(dataElems[i], _xmlNSPrefix + "invoke")) + if (dataElems[i].getNodeType() == Node_base::ELEMENT_NODE) { + initializeData(Element<std::string>(dataElems[i])); + } + } + } else if(_dataModel) { + // initialize current data elements + NodeSet<std::string> topDataElems = filterChildElements(_xmlNSPrefix + "data", filterChildElements(_xmlNSPrefix + "datamodel", _scxml)); + for (unsigned int i = 0; i < topDataElems.size(); i++) { + if (topDataElems[i].getNodeType() == Node_base::ELEMENT_NODE) + initializeData(Element<std::string>(topDataElems[i])); + } } - _historyValue[histId] = histStates; - - Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial"); - initialElem.setAttribute("generated", "true"); - Element<std::string> transitionElem = _document.createElementNS(_nsURL, "transition"); - transitionElem.setAttribute("target", histId); - initialElem.appendChild(transitionElem); - _scxml.appendChild(initialElem); - initialTransitions.push_back(transitionElem); - } else { - // try to get initial transition form initial element - initialTransitions = _xpath.evaluate("/" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", _scxml).asNodeSet(); - if (initialTransitions.size() == 0) { - Arabica::XPath::NodeSet<std::string> initialStates; - // fetch per draft - initialStates = getInitialStates(); - assert(initialStates.size() > 0); - for (int i = 0; i < initialStates.size(); i++) { - Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial"); - initialElem.setAttribute("generated", "true"); - Element<std::string> transitionElem = _document.createElementNS(_nsURL, "transition"); - transitionElem.setAttribute("target", ATTR(initialStates[i], "id")); - initialElem.appendChild(transitionElem); - _scxml.appendChild(initialElem); - initialTransitions.push_back(transitionElem); + // executeGlobalScriptElements + NodeSet<std::string> globalScriptElems = filterChildElements(_xmlNSPrefix + "script", _scxml); + for (unsigned int i = 0; i < globalScriptElems.size(); i++) { + if (_dataModel) { + executeContent(globalScriptElems[i]); } } - } - assert(initialTransitions.size() > 0); + NodeSet<std::string> initialTransitions; - monIter_t monIter = _monitors.begin(); - while(monIter != _monitors.end()) { - try { - (*monIter)->beforeTakingTransitions(shared_from_this(), initialTransitions); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling beforeTakingTransitions on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - } catch (...) { - LOG(ERROR) << "An exception occured when calling beforeTakingTransitions on monitors"; + if (_userDefinedStartConfiguration.size() > 0) { + // we emulate entering a given configuration by creating a pseudo deep history + Element<std::string> initHistory = _document.createElementNS(_nsURL, "history"); + initHistory.setAttribute("id", UUID::getUUID()); + initHistory.setAttribute("type", "deep"); + _scxml.insertBefore(initHistory, _scxml.getFirstChild()); + + std::string histId = ATTR(initHistory, "id"); + NodeSet<std::string> histStates; + for (int i = 0; i < _userDefinedStartConfiguration.size(); i++) { + histStates.push_back(getState(_userDefinedStartConfiguration[i])); + } + _historyValue[histId] = histStates; + + Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial"); + initialElem.setAttribute("generated", "true"); + Element<std::string> transitionElem = _document.createElementNS(_nsURL, "transition"); + transitionElem.setAttribute("target", histId); + initialElem.appendChild(transitionElem); + _scxml.appendChild(initialElem); + initialTransitions.push_back(transitionElem); + + } else { + // try to get initial transition form initial element + initialTransitions = _xpath.evaluate("/" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", _scxml).asNodeSet(); + if (initialTransitions.size() == 0) { + Arabica::XPath::NodeSet<std::string> initialStates; + // fetch per draft + initialStates = getInitialStates(); + assert(initialStates.size() > 0); + for (int i = 0; i < initialStates.size(); i++) { + Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial"); + initialElem.setAttribute("generated", "true"); + Element<std::string> transitionElem = _document.createElementNS(_nsURL, "transition"); + transitionElem.setAttribute("target", ATTR(initialStates[i], "id")); + initialElem.appendChild(transitionElem); + _scxml.appendChild(initialElem); + initialTransitions.push_back(transitionElem); + } + } } - monIter++; - } - enterStates(initialTransitions); -// _mutex.unlock(); + assert(initialTransitions.size() > 0); -// assert(hasLegalConfiguration()); - mainEventLoop(); + enterStates(initialTransitions); + // _mutex.unlock(); + // assert(hasLegalConfiguration()); + mainEventLoop(); + } catch (boost::bad_weak_ptr e) { + LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; + } // set datamodel to null from this thread if(_dataModel) _dataModel = DataModel(); @@ -192,22 +178,7 @@ void InterpreterDraft6::mainEventLoop() { } std::cout << std::endl; #endif - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { - try { - (*monIter)->beforeMicroStep(shared_from_this()); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling beforeMicroStep on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - _mutex.unlock(); - goto EXIT_INTERPRETER; - } catch (...) { - LOG(ERROR) << "An exception occured when calling beforeMicroStep on monitors"; - } - monIter++; - } - + enabledTransitions = selectEventlessTransitions(); if (enabledTransitions.size() == 0) { if (_internalQueue.size() == 0) { @@ -218,27 +189,20 @@ void InterpreterDraft6::mainEventLoop() { #if VERBOSE std::cout << "Received internal event " << _currEvent.name << std::endl; #endif + + // --- MONITOR: beforeProcessingEvent ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeProcessingEvent(shared_from_this(), _currEvent); + } USCXML_MONITOR_CATCH_BLOCK(beforeProcessingEvent) + } + if (_dataModel) _dataModel.setEvent(_currEvent); enabledTransitions = selectTransitions(_currEvent.name); } } if (!enabledTransitions.empty()) { - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { - try { - (*monIter)->beforeTakingTransitions(shared_from_this(), enabledTransitions); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling beforeTakingTransitions on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - _mutex.unlock(); - goto EXIT_INTERPRETER; - } catch (...) { - LOG(ERROR) << "An exception occured when calling beforeTakingTransitions on monitors"; - } - monIter++; - } // test 403b enabledTransitions.to_document_order(); microstep(enabledTransitions); @@ -269,20 +233,14 @@ void InterpreterDraft6::mainEventLoop() { monIter = _monitors.begin(); // if (!_sendQueue || _sendQueue->isEmpty()) { - while(monIter != _monitors.end()) { + + // --- MONITOR: onStableConfiguration ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->onStableConfiguration(shared_from_this()); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling onStableConfiguration on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - _mutex.unlock(); - goto EXIT_INTERPRETER; - } catch (...) { - LOG(ERROR) << "An exception occured when calling onStableConfiguration on monitors"; - } - monIter++; + } USCXML_MONITOR_CATCH_BLOCK(onStableConfiguration) } + // } _mutex.unlock(); @@ -303,6 +261,13 @@ void InterpreterDraft6::mainEventLoop() { if (!_running) goto EXIT_INTERPRETER; + // --- MONITOR: beforeProcessingEvent ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeProcessingEvent(shared_from_this(), _currEvent); + } USCXML_MONITOR_CATCH_BLOCK(beforeProcessingEvent) + } + if (_dataModel && iequals(_currEvent.name, "cancel.invoke." + _sessionId)) break; @@ -360,19 +325,11 @@ void InterpreterDraft6::mainEventLoop() { } EXIT_INTERPRETER: - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { + // --- MONITOR: beforeCompletion ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->beforeCompletion(shared_from_this()); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling beforeCompletion on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - exitInterpreter(); - } catch (...) { - LOG(ERROR) << "An exception occured when calling beforeCompletion on monitors"; - } - monIter++; + } USCXML_MONITOR_CATCH_BLOCK(beforeCompletion) } exitInterpreter(); @@ -384,19 +341,11 @@ EXIT_INTERPRETER: } } - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { + // --- MONITOR: afterCompletion ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->afterCompletion(shared_from_this()); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling afterCompletion on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - exitInterpreter(); - } catch (...) { - LOG(ERROR) << "An exception occured when calling afterCompletion on monitors"; - } - monIter++; + } USCXML_MONITOR_CATCH_BLOCK(afterCompletion) } } @@ -635,9 +584,45 @@ void InterpreterDraft6::microstep(const Arabica::XPath::NodeSet<std::string>& en std::cout << std::endl; #endif + // --- MONITOR: beforeMicroStep ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeMicroStep(shared_from_this()); + } USCXML_MONITOR_CATCH_BLOCK(beforeMicroStep) + } + exitStates(enabledTransitions); - executeContent(enabledTransitions); + + monIter_t monIter; + for (int i = 0; i < enabledTransitions.size(); i++) { + Element<std::string> transition(enabledTransitions[i]); + + // --- MONITOR: beforeTakingTransitions ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeTakingTransition(shared_from_this(), transition); + } USCXML_MONITOR_CATCH_BLOCK(beforeTakingTransitions) + } + + executeContent(transition); + + // --- MONITOR: afterTakingTransitions ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->afterTakingTransition(shared_from_this(), transition); + } USCXML_MONITOR_CATCH_BLOCK(afterTakingTransitions) + } + } + enterStates(enabledTransitions); + + // --- MONITOR: afterMicroStep ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->afterMicroStep(shared_from_this()); + } USCXML_MONITOR_CATCH_BLOCK(afterMicroStep) + } + } void InterpreterDraft6::exitInterpreter() { @@ -734,21 +719,6 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e std::cout << std::endl; #endif - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { - try { - (*monIter)->beforeExitingStates(shared_from_this(), statesToExit); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling beforeExitingStates on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - exitInterpreter(); - } catch (...) { - LOG(ERROR) << "An exception occured when calling beforeExitingStates on monitors"; - } - monIter++; - } - for (int i = 0; i < statesToExit.size(); i++) { NodeSet<std::string> histories = filterChildElements(_xmlNSPrefix + "history", statesToExit[i]); for (int j = 0; j < histories.size(); j++) { @@ -777,11 +747,26 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e } for (int i = 0; i < statesToExit.size(); i++) { + // --- MONITOR: beforeExitingState ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeExitingState(shared_from_this(), Element<std::string>(statesToExit[i])); + } USCXML_MONITOR_CATCH_BLOCK(beforeExitingState) + } + NodeSet<std::string> onExits = filterChildElements(_xmlNSPrefix + "onExit", statesToExit[i]); for (int j = 0; j < onExits.size(); j++) { Element<std::string> onExitElem = (Element<std::string>)onExits[j]; executeContent(onExitElem); } + + // --- MONITOR: afterExitingState ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->afterExitingState(shared_from_this(), Element<std::string>(statesToExit[i])); + } USCXML_MONITOR_CATCH_BLOCK(afterExitingState) + } + NodeSet<std::string> invokes = filterChildElements(_xmlNSPrefix + "invoke", statesToExit[i]); for (int j = 0; j < invokes.size(); j++) { Element<std::string> invokeElem = (Element<std::string>)invokes[j]; @@ -797,22 +782,6 @@ void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& e _configuration = NodeSet<std::string>(); _configuration.insert(_configuration.end(), tmp.begin(), tmp.end()); } - - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { - try { - (*monIter)->afterExitingStates(shared_from_this()); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling afterExitingStates on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - exitInterpreter(); - } catch (...) { - LOG(ERROR) << "An exception occured when calling afterExitingStates on monitors"; - } - monIter++; - } - } void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { @@ -926,23 +895,16 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& std::cout << std::endl; #endif - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { - try { - (*monIter)->beforeEnteringStates(shared_from_this(), statesToEnter); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling beforeEnteringStates on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - exitInterpreter(); - } catch (...) { - LOG(ERROR) << "An exception occured when calling beforeEnteringStates on monitors"; - } - monIter++; - } - for (int i = 0; i < statesToEnter.size(); i++) { Element<std::string> stateElem = (Element<std::string>)statesToEnter[i]; + + // --- MONITOR: beforeEnteringState ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->beforeEnteringState(shared_from_this(), stateElem); + } USCXML_MONITOR_CATCH_BLOCK(beforeEnteringState) + } + _configuration.push_back(stateElem); _statesToInvoke.push_back(stateElem); if (_binding == LATE && stateElem.getAttribute("isFirstEntry").size() > 0) { @@ -960,6 +922,13 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& NodeSet<std::string> onEntryElems = filterChildElements(_xmlNSPrefix + "onEntry", stateElem); executeContent(onEntryElems, false); + // --- MONITOR: afterEnteringState ------------------------------ + for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { + try { + (*monIter)->afterEnteringState(shared_from_this(), stateElem); + } USCXML_MONITOR_CATCH_BLOCK(afterEnteringState) + } + if (isMember(stateElem, statesForDefaultEntry)) { // execute initial transition content for compound states Arabica::XPath::NodeSet<std::string> transitions = _xpath.evaluate("" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", stateElem).asNodeSet(); @@ -996,22 +965,6 @@ void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& _done = true; } } - - monIter = _monitors.begin(); - while(monIter != _monitors.end()) { - try { - (*monIter)->afterEnteringStates(shared_from_this()); - } catch (Event e) { - LOG(ERROR) << "Syntax error when calling afterEnteringStates on monitors: " << std::endl << e << std::endl; - } catch (boost::bad_weak_ptr e) { - LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; - return; - } catch (...) { - LOG(ERROR) << "An exception occured when calling afterEnteringStates on monitors"; - } - monIter++; - } - } void InterpreterDraft6::addStatesToEnter(const Node<std::string>& state, diff --git a/src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp b/src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp index 19c5907..5237977 100644 --- a/src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp +++ b/src/uscxml/plugins/invoker/imap/IMAPInvoker.cpp @@ -17,6 +17,7 @@ * @endcond */ +#define NOMINMAX // and have MSVC die in a fire for defining min macro #include "IMAPInvoker.h" #include <glog/logging.h> @@ -71,7 +72,7 @@ size_t IMAPInvoker::writeCurlData(void *ptr, size_t size, size_t nmemb, void *us IMAPContext* ctx = (IMAPContext*)userdata; - size_t toWrite = std::min(ctx->outContent.length() - ctx->readPtr, size * nmemb); + size_t toWrite = (std::min)(ctx->outContent.length() - ctx->readPtr, size * nmemb); if (toWrite > 0) { memcpy (ptr, ctx->outContent.c_str() + ctx->readPtr, toWrite); ctx->readPtr += toWrite; @@ -100,7 +101,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "mailbox", args->mailbox); ctx = new IMAPContext(); - ctx->command = IMAPContext::SELECT; + ctx->command = IMAPContext::IMAP_SELECT; ctx->arguments = args; } else if (iequals(req.name, "examine")) { @@ -108,7 +109,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "mailbox", args->mailbox); ctx = new IMAPContext(); - ctx->command = IMAPContext::EXAMINE; + ctx->command = IMAPContext::IMAP_EXAMINE; ctx->arguments = args; } else if (iequals(req.name, "delete")) { @@ -116,7 +117,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "mailbox", args->mailbox); ctx = new IMAPContext(); - ctx->command = IMAPContext::DELETE; + ctx->command = IMAPContext::IMAP_DELETE; ctx->arguments = args; } else if (iequals(req.name, "rename")) { @@ -125,7 +126,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "name", args->newName); ctx = new IMAPContext(); - ctx->command = IMAPContext::RENAME; + ctx->command = IMAPContext::IMAP_RENAME; ctx->arguments = args; } else if (iequals(req.name, "subscribe")) { @@ -133,7 +134,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "mailbox", args->mailbox); ctx = new IMAPContext(); - ctx->command = IMAPContext::SUBSCRIBE; + ctx->command = IMAPContext::IMAP_SUBSCRIBE; ctx->arguments = args; } else if (iequals(req.name, "unsubscribe")) { @@ -141,7 +142,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "mailbox", args->mailbox); ctx = new IMAPContext(); - ctx->command = IMAPContext::UNSUBSCRIBE; + ctx->command = IMAPContext::IMAP_UNSUBSCRIBE; ctx->arguments = args; } else if (iequals(req.name, "list")) { @@ -150,7 +151,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "reference", args->refName); ctx = new IMAPContext(); - ctx->command = IMAPContext::LIST; + ctx->command = IMAPContext::IMAP_LIST; ctx->arguments = args; } else if (iequals(req.name, "lsub")) { @@ -159,7 +160,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "reference", args->refName); ctx = new IMAPContext(); - ctx->command = IMAPContext::LSUB; + ctx->command = IMAPContext::IMAP_LSUB; ctx->arguments = args; } else if (iequals(req.name, "status")) { @@ -168,7 +169,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "dataitems", args->dataItems); ctx = new IMAPContext(); - ctx->command = IMAPContext::STATUS; + ctx->command = IMAPContext::IMAP_STATUS; ctx->arguments = args; } else if (iequals(req.name, "append")) { @@ -182,28 +183,28 @@ void IMAPInvoker::send(const SendRequest& req) { } ctx = new IMAPContext(); - ctx->command = IMAPContext::APPEND; + ctx->command = IMAPContext::IMAP_APPEND; ctx->arguments = args; } else if (iequals(req.name, "check")) { IMAPContext::Check* args = new IMAPContext::Check(); ctx = new IMAPContext(); - ctx->command = IMAPContext::CHECK; + ctx->command = IMAPContext::IMAP_CHECK; ctx->arguments = args; } else if (iequals(req.name, "close")) { IMAPContext::Close* args = new IMAPContext::Close(); ctx = new IMAPContext(); - ctx->command = IMAPContext::CLOSE; + ctx->command = IMAPContext::IMAP_CLOSE; ctx->arguments = args; } else if (iequals(req.name, "expunge")) { IMAPContext::Expunge* args = new IMAPContext::Expunge(); ctx = new IMAPContext(); - ctx->command = IMAPContext::EXPUNGE; + ctx->command = IMAPContext::IMAP_EXPUNGE; ctx->arguments = args; } else if (iequals(req.name, "search")) { @@ -212,7 +213,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "criteria", args->criteria); ctx = new IMAPContext(); - ctx->command = IMAPContext::SEARCH; + ctx->command = IMAPContext::IMAP_SEARCH; ctx->arguments = args; } else if (iequals(req.name, "fetch")) { @@ -221,7 +222,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "itemnames", args->itemNames); ctx = new IMAPContext(); - ctx->command = IMAPContext::FETCH; + ctx->command = IMAPContext::IMAP_FETCH; ctx->arguments = args; } else if (iequals(req.name, "store")) { @@ -231,7 +232,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "values", args->values); ctx = new IMAPContext(); - ctx->command = IMAPContext::STORE; + ctx->command = IMAPContext::IMAP_STORE; ctx->arguments = args; } else if (iequals(req.name, "copy")) { @@ -240,7 +241,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "sequence", args->sequence); ctx = new IMAPContext(); - ctx->command = IMAPContext::COPY; + ctx->command = IMAPContext::IMAP_COPY; ctx->arguments = args; } else if (iequals(req.name, "uid")) { @@ -249,7 +250,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "arguments", args->arguments); ctx = new IMAPContext(); - ctx->command = IMAPContext::UID; + ctx->command = IMAPContext::IMAP_UID; ctx->arguments = args; } else if (boost::istarts_with(req.name, "x")) { @@ -258,7 +259,7 @@ void IMAPInvoker::send(const SendRequest& req) { Event::getParam(req.params, "arguments", args->arguments); ctx = new IMAPContext(); - ctx->command = IMAPContext::XEXTENSION; + ctx->command = IMAPContext::IMAP_XEXTENSION; ctx->arguments = args; } @@ -322,74 +323,74 @@ void IMAPInvoker::process(IMAPContext* ctx) { std::stringstream cmdSS; switch (ctx->command) { - case IMAPContext::SELECT: { + case IMAPContext::IMAP_SELECT: { IMAPContext::Select* cmd = (IMAPContext::Select*)ctx->arguments; cmdSS << "SELECT " << "\"" << cmd->mailbox << "\""; break; } - case IMAPContext::EXAMINE: { + case IMAPContext::IMAP_EXAMINE: { IMAPContext::Examine* cmd = (IMAPContext::Examine*)ctx->arguments; cmdSS << "EXAMINE " << "\"" << cmd->mailbox << "\""; break; } - case IMAPContext::CREATE: { + case IMAPContext::IMAP_CREATE: { IMAPContext::Create* cmd = (IMAPContext::Create*)ctx->arguments; cmdSS << "CREATE " << "\"" << cmd->mailbox << "\""; break; } - case IMAPContext::DELETE: { + case IMAPContext::IMAP_DELETE: { IMAPContext::Delete* cmd = (IMAPContext::Delete*)ctx->arguments; cmdSS << "DELETE " << "\"" << cmd->mailbox << "\""; break; } - case IMAPContext::RENAME: { + case IMAPContext::IMAP_RENAME: { IMAPContext::Rename* cmd = (IMAPContext::Rename*)ctx->arguments; cmdSS << "RENAME " << "\"" << cmd->mailbox << "\" \"" << cmd->newName << "\""; break; } - case IMAPContext::SUBSCRIBE: { + case IMAPContext::IMAP_SUBSCRIBE: { IMAPContext::Subscribe* cmd = (IMAPContext::Subscribe*)ctx->arguments; cmdSS << "SUBSCRIBE " << "\"" << cmd->mailbox << "\""; break; } - case IMAPContext::UNSUBSCRIBE: { + case IMAPContext::IMAP_UNSUBSCRIBE: { IMAPContext::Unsubscribe* cmd = (IMAPContext::Unsubscribe*)ctx->arguments; cmdSS << "UNSUBSCRIBE " << "\"" << cmd->mailbox << "\""; break; } - case IMAPContext::LIST: { + case IMAPContext::IMAP_LIST: { IMAPContext::List* cmd = (IMAPContext::List*)ctx->arguments; cmdSS << "LIST " << "\"" << cmd->mailbox << "\" \"" << cmd->refName << "\""; break; } - case IMAPContext::LSUB: { + case IMAPContext::IMAP_LSUB: { IMAPContext::LSub* cmd = (IMAPContext::LSub*)ctx->arguments; cmdSS << "LSUB " << "\"" << cmd->mailbox << "\" \"" << cmd->refName << "\""; break; } - case IMAPContext::STATUS: { + case IMAPContext::IMAP_STATUS: { IMAPContext::Status* cmd = (IMAPContext::Status*)ctx->arguments; cmdSS << "STATUS " << "\"" << cmd->mailbox << "\" (" << cmd->dataItems << ")"; break; } - case IMAPContext::APPEND: { + case IMAPContext::IMAP_APPEND: { IMAPContext::Append* cmd = (IMAPContext::Append*)ctx->arguments; cmdSS << "APPEND " << "\"" << cmd->mailbox << "\" (" << cmd->flags << ") {" << cmd->dateTime << "}"; break; } - case IMAPContext::CHECK: { + case IMAPContext::IMAP_CHECK: { cmdSS << "CHECK"; break; } - case IMAPContext::CLOSE: { + case IMAPContext::IMAP_CLOSE: { cmdSS << "CLOSE"; break; } - case IMAPContext::EXPUNGE: { + case IMAPContext::IMAP_EXPUNGE: { cmdSS << "EXPUNGE"; break; } - case IMAPContext::SEARCH: { + case IMAPContext::IMAP_SEARCH: { IMAPContext::Search* cmd = (IMAPContext::Search*)ctx->arguments; cmdSS << "SEARCH "; if (cmd->charSet.size() > 0) { @@ -398,27 +399,27 @@ void IMAPInvoker::process(IMAPContext* ctx) { cmdSS << cmd->criteria; break; } - case IMAPContext::FETCH: { + case IMAPContext::IMAP_FETCH: { IMAPContext::Fetch* cmd = (IMAPContext::Fetch*)ctx->arguments; cmdSS << "FETCH " << cmd->sequence << " " << cmd->itemNames; break; } - case IMAPContext::STORE: { + case IMAPContext::IMAP_STORE: { IMAPContext::Store* cmd = (IMAPContext::Store*)ctx->arguments; cmdSS << "STORE " << cmd->sequence << " " << cmd->itemNames << " " << cmd->values; break; } - case IMAPContext::COPY: { + case IMAPContext::IMAP_COPY: { IMAPContext::Copy* cmd = (IMAPContext::Copy*)ctx->arguments; cmdSS << "COPY " << "\"" << cmd->mailbox << "\" " << cmd->sequence; break; } - case IMAPContext::UID: { + case IMAPContext::IMAP_UID: { IMAPContext::UId* cmd = (IMAPContext::UId*)ctx->arguments; cmdSS << "UID " << cmd->command << " " << cmd->arguments; break; } - case IMAPContext::XEXTENSION: { + case IMAPContext::IMAP_XEXTENSION: { IMAPContext::XExtension* cmd = (IMAPContext::XExtension*)ctx->arguments; cmdSS << cmd->command << " " << cmd->arguments; break; diff --git a/src/uscxml/plugins/invoker/imap/IMAPInvoker.h b/src/uscxml/plugins/invoker/imap/IMAPInvoker.h index 3c367da..94d199b 100644 --- a/src/uscxml/plugins/invoker/imap/IMAPInvoker.h +++ b/src/uscxml/plugins/invoker/imap/IMAPInvoker.h @@ -56,27 +56,27 @@ protected: public: enum Cmd { // valid in authenticated state - SELECT, - EXAMINE, - CREATE, - DELETE, - RENAME, - SUBSCRIBE, - UNSUBSCRIBE, - LIST, - LSUB, - STATUS, - APPEND, + IMAP_SELECT, + IMAP_EXAMINE, + IMAP_CREATE, + IMAP_DELETE, + IMAP_RENAME, + IMAP_SUBSCRIBE, + IMAP_UNSUBSCRIBE, + IMAP_LIST, + IMAP_LSUB, + IMAP_STATUS, + IMAP_APPEND, // valid in selected state - CHECK, - CLOSE, - EXPUNGE, - SEARCH, - FETCH, - STORE, - COPY, - UID, - XEXTENSION, + IMAP_CHECK, + IMAP_CLOSE, + IMAP_EXPUNGE, + IMAP_SEARCH, + IMAP_FETCH, + IMAP_STORE, + IMAP_COPY, + IMAP_UID, + IMAP_XEXTENSION, }; struct MailboxOp { diff --git a/src/uscxml/plugins/invoker/smtp/SMTPInvoker.cpp b/src/uscxml/plugins/invoker/smtp/SMTPInvoker.cpp index 3e130a0..1248733 100644 --- a/src/uscxml/plugins/invoker/smtp/SMTPInvoker.cpp +++ b/src/uscxml/plugins/invoker/smtp/SMTPInvoker.cpp @@ -59,7 +59,7 @@ size_t SMTPInvoker::writeCurlData(void *ptr, size_t size, size_t nmemb, void *us SMTPContext* ctx = (SMTPContext*)userdata; - size_t toWrite = std::min(ctx->content.length() - ctx->readPtr, size * nmemb); + size_t toWrite = (std::min)(ctx->content.length() - ctx->readPtr, size * nmemb); if (toWrite > 0) { memcpy (ptr, ctx->content.c_str() + ctx->readPtr, toWrite); ctx->readPtr += toWrite; diff --git a/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp b/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp index a6a2df3..4129413 100644 --- a/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp +++ b/src/uscxml/plugins/invoker/umundo/UmundoInvoker.cpp @@ -369,7 +369,7 @@ bool UmundoInvoker::protobufToData(Data& data, const google::protobuf::Message& const google::protobuf::Descriptor* desc = msg.GetDescriptor(); const google::protobuf::Reflection* reflect = msg.GetReflection(); - data.compound["type"] = Data(desc->name(), Data::VERBATIM); + data.compound["protobufType"] = Data(desc->name(), Data::VERBATIM); for (int i = 0; i < desc->field_count(); i++) { const google::protobuf::FieldDescriptor* fieldDesc = desc->field(i); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4f1ec70..9d6c057 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -113,11 +113,6 @@ set_target_properties(test-datamodel PROPERTIES FOLDER "Tests") # set_target_properties(test-mmi PROPERTIES FOLDER "Tests") # endif() -add_executable(scxml-test-framework-client - src/scxml-test-framework-client.cpp) - target_link_libraries(scxml-test-framework-client uscxml) -set_target_properties(scxml-test-framework-client PROPERTIES FOLDER "Tests") - # add_executable(test-curl-multi-api src/test-curl-multi-api.cpp) # target_link_libraries(test-curl-multi-api uscxml) # add_test(test-curl-multi-api ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-completion) diff --git a/test/src/scxml-test-framework-client.cpp b/test/src/scxml-test-framework-client.cpp deleted file mode 100644 index d6ffd96..0000000 --- a/test/src/scxml-test-framework-client.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include "uscxml/Interpreter.h" -#include "uscxml/server/HTTPServer.h" -#include "uscxml/DOMUtils.h" -#include <sstream> - -extern "C" { -#include "jsmn.h" // minimal json parser -} - -#include <event2/keyvalq_struct.h> -#include <event2/buffer.h> - -/** - POST /foo HTTP/1.1 - host: localhost:9000 - accept: application/json - content-type: application/json - content-length: 92 - Connection: keep-alive - - {"load":"http://localhost:9999/scxml-test-framework/test/targetless-transition/test3.scxml"} -*/ - -class TestIOProcessor : public uscxml::HTTPServlet, public uscxml::InterpreterMonitor { -public: - - static int lastToken; - static bool alreadyAnswered; // we need this for delayed events - static std::map<std::string, std::pair<uscxml::Interpreter, uscxml::HTTPServer::Request> > _interpreters; - - TestIOProcessor() {} - - virtual void beforeCompletion(uscxml::Interpreter interpreter) { - onStableConfiguration(interpreter); - } - - virtual void afterCompletion(uscxml::Interpreter interpreter) { - _interpreters[interpreter.getName()].second.evhttpReq = NULL; - } - virtual void beforeMicroStep(uscxml::Interpreter interpreter) {} - virtual void beforeTakingTransitions(uscxml::Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& transitions) {} - - virtual void beforeEnteringStates(uscxml::Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& statesToEnter) { - std::cout << "Entering states: "; - for (int i = 0; i < statesToEnter.size(); i++) { - std::cout << ATTR(statesToEnter[i], "id") << ", "; - } - std::cout << std::endl; - } - - virtual void afterEnteringStates(uscxml::Interpreter interpreter) { - std::cout << "After entering states: "; - for (int i = 0; i < interpreter.getConfiguration().size(); i++) { - std::cout << ATTR(interpreter.getConfiguration()[i], "id") << ", "; - } - std::cout << std::endl; - } - - virtual void beforeExitingStates(uscxml::Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& statesToExit) { - std::cout << "Configuration: "; - for (int i = 0; i < interpreter.getConfiguration().size(); i++) { - std::cout << ATTR(interpreter.getConfiguration()[i], "id") << ", "; - } - std::cout << std::endl; - std::cout << "Exiting states: "; - for (int i = 0; i < statesToExit.size(); i++) { - std::cout << ATTR(statesToExit[i], "id") << ", "; - } - std::cout << std::endl; - } - - virtual void afterExitingStates(uscxml::Interpreter interpreter) { - std::cout << "After exiting states: "; - for (int i = 0; i < interpreter.getConfiguration().size(); i++) { - std::cout << ATTR(interpreter.getConfiguration()[i], "id") << ", "; - } - std::cout << std::endl; - } - - virtual void onStableConfiguration(uscxml::Interpreter interpreter) { - if (alreadyAnswered) - return; - - Arabica::XPath::NodeSet<std::string> configuration = interpreter.getConfiguration(); - - uscxml::Data reply; - reply.compound["sessionToken"] = uscxml::Data(interpreter.getName()); - std::string seperator; - for (size_t i = 0; i < configuration.size(); i++) { - if (uscxml::Interpreter::isAtomic(configuration[i])) - reply.compound["nextConfiguration"].array.push_back(uscxml::Data(ATTR(configuration[i], "id"), uscxml::Data::VERBATIM)); - } - - std::cout << "---- reply:" << std::endl; - std::cout << reply << std::endl; - - std::stringstream replyString; - replyString << reply; - - alreadyAnswered = true; - - uscxml::HTTPServer::Request httpRequest = _interpreters[interpreter.getName()].second; - uscxml::HTTPServer::Reply httpReply(httpRequest); - httpReply.content = replyString.str(); - uscxml::HTTPServer::reply(httpReply); - - } - - bool httpRecvRequest(const uscxml::HTTPServer::Request& request) { - -// uscxml::HTTPServer::Reply httpReply(request); -// uscxml::HTTPServer::reply(httpReply); -// return; - - std::cout << "---- received:" << std::endl; -// evhttp_request_own(request.curlReq); - - std::cout << request.data.compound.at("content").atom << std::endl; - uscxml::Data jsonReq = uscxml::Data::fromJSON(request.data.compound.at("content").atom); - std::cout << jsonReq << std::endl; - - - // is this a load request? - if (jsonReq.compound.find("load") != jsonReq.compound.end()) { - std::string filename = jsonReq.compound["load"].atom; - std::cout << "Starting Interpreter with " << filename << std::endl; - alreadyAnswered = false; - - std::map<std::string, std::pair<uscxml::Interpreter, uscxml::HTTPServer::Request> >::iterator interpreterIter = _interpreters.begin(); - while(interpreterIter != _interpreters.end()) { -// if (interpreterIter->second.second.curlReq == NULL) { - _interpreters.erase(interpreterIter++); -// } else { -// interpreterIter++; -// } - } - - - uscxml::Interpreter interpreter = uscxml::Interpreter::fromURI(filename); - if (interpreter) { - std::string token = uscxml::toStr(lastToken++); - assert(_interpreters.find(token) == _interpreters.end()); - interpreter.setName(token); - interpreter.addMonitor(this); - _interpreters[token] = std::make_pair(interpreter, request); - interpreter.start(); - } - return true; - } - - if(jsonReq.compound.find("event") != jsonReq.compound.end()) { - assert(jsonReq.compound["event"].compound.find("sessionToken") != jsonReq.compound["event"].compound.end()); - std::string token = jsonReq.compound["event"].compound["sessionToken"].atom; - assert(_interpreters.find(token) != _interpreters.end()); - uscxml::Event event; - event.eventType = uscxml::Event::INTERNAL; - event.name = jsonReq.compound["event"].compound["name"].atom; - std::cout << "Sending event " << event << std::endl; -// evhttp_request_free(_interpreters[token].second); - alreadyAnswered = false; - _interpreters[token].second = request; - _interpreters[token].first.receive(event); - } - return true; - } - - void setURL(const std::string& url) { - std::cout << "Listening at " << url << std::endl; - } -}; - -int TestIOProcessor::lastToken; -bool TestIOProcessor::alreadyAnswered; -std::map<std::string, std::pair<uscxml::Interpreter, uscxml::HTTPServer::Request> > TestIOProcessor::_interpreters; - -int main(int argc, char** argv) { - TestIOProcessor* testServer = new TestIOProcessor(); - uscxml::HTTPServer::registerServlet("test", testServer); - - while(true) - tthread::this_thread::sleep_for(tthread::chrono::milliseconds(20)); - -}
\ No newline at end of file |