From d2181f41ad8d570f330498485a7cf3b0a043a7a4 Mon Sep 17 00:00:00 2001
From: Dmitry Simonenko <pmwkaa@gmail.com>
Date: Fri, 27 Jan 2012 14:44:51 +0400
Subject: [PATCH] Blueprint implementation: Add cmake option to support
 compilation with external luajit
 (https://blueprints.launchpad.net/tarantool/+spec/compile-with-external-luajit)

---
 CMakeLists.txt             |  10 ++--
 cmake/luajit.cmake         | 115 +++++++++++++++++++++++++++++++++++++
 cmake/luatest.cpp          |  41 +++++++++++++
 core/CMakeLists.txt        |   4 +-
 core/admin.m               |   8 +--
 core/admin.rl              |   8 +--
 core/tarantool_lua.m       |   8 +--
 include/config.h.cmake     |  10 +++-
 mod/box/box_lua.m          |   8 +--
 third_party/CMakeLists.txt |  73 +++++++++++++----------
 10 files changed, 232 insertions(+), 53 deletions(-)
 create mode 100644 cmake/luajit.cmake
 create mode 100644 cmake/luatest.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6904f17f5f..a769f04dcd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,6 +6,7 @@ include(CheckLibraryExists)
 include(CheckIncludeFile)
 include(CheckCCompilerFlag)
 include(CheckSymbolExists)
+include(CheckCXXSourceRuns)
 include(TestBigEndian)
 
 find_program(ECHO echo)
@@ -17,10 +18,8 @@ find_program(RAGEL ragel)
 find_program(CONFETTI confetti)
 find_program(LD ld)
 
-set(luadir ${PROJECT_BINARY_DIR}/third_party/luajit/src)
-link_directories(${luadir})
-
 include(cmake/check_objective_c_compiler.cmake)
+include(cmake/luajit.cmake)
 
 #
 # This instructs the rest of the build system what product
@@ -30,7 +29,6 @@ set (TARANTOOL_PRODUCT "box")
 set (TARANTOOL_MODULES "box")
 set (TARANTOOL_CLIENTS "")
 
-
 #
 # Set default build type to Debug. This is to ease a developer's
 # life. Release binaries are built by BuildBot automatically anyway.
@@ -184,6 +182,9 @@ endif()
 #
 # Now handle all configuration options.
 #
+
+# luajit options are defined in cmake/luajit.cmake
+
 option(ENABLE_CLIENT "Enable building of console client" Off)
 if (ENABLE_CLIENT)
     set (TARANTOOL_CLIENTS ${TARANTOOL_CLIENTS} "tarantool")
@@ -280,6 +281,7 @@ message (STATUS "${TARANTOOL_MODULES}.")
 message (STATUS "Please check out CMakeCache.txt to view or modify configuration results.")
 message (STATUS "")
 message (STATUS "*** The following options are on in this configuration: ***")
+message (STATUS "ENABLE_LUAJIT: ${ENABLE_LUAJIT}")
 message (STATUS "ENABLE_CLIENT: ${ENABLE_CLIENT}")
 message (STATUS "ENABLE_GCOV: ${ENABLE_GCOV}")
 message (STATUS "ENABLE_TRACE: ${ENABLE_TRACE}")
diff --git a/cmake/luajit.cmake b/cmake/luajit.cmake
new file mode 100644
index 0000000000..d34baefbb7
--- /dev/null
+++ b/cmake/luajit.cmake
@@ -0,0 +1,115 @@
+
+#
+# luajit configuration file.
+#
+
+#
+# luajit defaults.
+#
+set (LUAJIT_DEFAULT_PREFIX "${PROJECT_BINARY_DIR}/third_party/luajit/src")
+set (LUAJIT_DEFAULT_LIB "${LUAJIT_DEFAULT_PREFIX}/libluajit.a")
+set (LUAJIT_TESTED 0)
+
+macro (luajit_set_default)
+    set (LUAJIT_PREFIX "${LUAJIT_DEFAULT_PREFIX}")
+    set (LUAJIT_INCLUDE "${LUAJIT_DEFAULT_PREFIX}")
+    set (LUAJIT_LIB "${LUAJIT_DEFAULT_LIB}")
+    set (ENABLE_LUAJIT ON)
+endmacro()
+
+#
+# luajit search macro.
+#
+macro (luajit_find isdefault)
+    if (${isdefault} STREQUAL "True")
+        find_path (LUAJIT_INCLUDE "lua.h")
+        find_library (LUAJIT_LIB "luajit")
+    else()
+        find_path (LUAJIT_INCLUDE "lua.h" ${LUAJIT_PREFIX} NO_DEFAULT_PATH)
+        find_library (LUAJIT_LIB "luajit" ${LUAJIT_PREFIX} NO_DEFAULT_PATH)
+    endif()
+endmacro()
+
+#
+# luajit testing routine
+# (see cmake/luatest.cpp for description).
+#
+macro (luajit_test)
+    file (READ "${CMAKE_SOURCE_DIR}/cmake/luatest.cpp" LUAJIT_TEST)
+    set (CMAKE_REQUIRED_LIBRARIES "${LUAJIT_LIB}")
+    if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+        set (CMAKE_REQUIRED_LIBRARIES "-ldl ${CMAKE_REQUIRED_LIBRARIES}")
+    endif()
+    CHECK_CXX_SOURCE_RUNS ("${LUAJIT_TEST}" LUAJIT_RUNS)
+    set (LUAJIT_TESTED "${LUAJIT_RUNS}")
+    unset (LUAJIT_RUNS)
+    unset (CMAKE_REQUIRED_LIBRARIES)
+endmacro()
+
+#
+# Check if there is system luajit availaible and
+# it can be used with server exception's (determined by test).
+#
+macro (luajit_try_system)
+    luajit_find(True)
+    if (LUAJIT_INCLUDE AND LUAJIT_LIB)
+        message (STATUS "Found system luajit.")
+        luajit_test()
+        if (LUAJIT_TESTED)
+            message (STATUS "System luajit is suitable for use.")
+        else()
+            message (WARNING "System luajit is NOT suitable for use, using default.")
+            # in case of system inability to use
+            # system luajit, setting prefix.
+	    luajit_set_default()
+        endif()
+    else()
+        message (STATUS "Not found system luajit, using default.")
+        luajit_set_default()
+    endif()
+endmacro()
+
+#
+# Check if there is usable luajit in specified prefix
+# path.
+#
+macro (luajit_try_prefix)
+    luajit_find(False)
+    if (LUAJIT_INCLUDE AND LUAJIT_LIB)
+        include_directories("${LUAJIT_INCLUDE}")
+        link_directories("${LUAJIT_PREFIX}")
+        luajit_test()
+        if (LUAJIT_TESTED)
+            message (STATUS "Supplied luajit is suitable for use.")
+        else()
+            message (FATAL_ERROR "Supplied luajit is NOT suitable for use.")
+        endif()
+    else()
+        message (FATAL_ERROR "Couldn't find luajit in '${LUAJIT_PREFIX}'")
+    endif()
+endmacro()
+
+#
+# luajit options.
+#
+option(ENABLE_LUAJIT "Enable building of shipped luajit" OFF)
+option(LUAJIT_PREFIX "luajit path" "")
+
+if (LUAJIT_PREFIX AND ENABLE_LUAJIT)
+    message (FATAL_ERROR "Only one of LUAJIT_PREFIX or ENABLE_LUAJIT "
+                         "options can be specified.")
+endif()
+
+if (LUAJIT_PREFIX)
+    # trying to build with specified luajit.
+    luajit_try_prefix()
+elseif (NOT ENABLE_LUAJIT)
+    # trying to build with system luajit, macro can turn on
+    # building of luajit shipped with server.
+    luajit_try_system()
+else()
+    luajit_set_default()
+endif()
+
+message (STATUS "Luajit include: ${LUAJIT_INCLUDE}")
+message (STATUS "Luajit lib: ${LUAJIT_LIB}")
diff --git a/cmake/luatest.cpp b/cmake/luatest.cpp
new file mode 100644
index 0000000000..453d6e1f89
--- /dev/null
+++ b/cmake/luatest.cpp
@@ -0,0 +1,41 @@
+
+/*
+ * Test for determining luajit behavior on current platform.
+ *
+ * Luajit uses different stack unwinding mechanisms on x86/PPC
+ * and x64 and a compilation flags.
+ *
+ * Using default mechanism under x86 (32-bit) can cause troubles
+ * with raising and propogation exceptions from error handlers -
+ * it starts recursively call panic error handler.
+ *
+*/
+
+#include <cstdlib>
+#include <lua.hpp>
+
+static int panic = 0;
+
+static int lua_panic_cb(lua_State *L) {
+	if (!panic++)
+		throw 0;
+	abort();
+	return 0;
+}
+
+int
+main(int argc, char * argv[])
+{
+	lua_State *L = luaL_newstate();
+	if (L == NULL)
+		return 1;
+	lua_atpanic(L, lua_panic_cb);
+	try {
+		lua_pushstring(L, "uncallable");
+		lua_call(L, 0, LUA_MULTRET);
+	} catch (...) {
+		/* if we luck, we should get here. */
+	}
+	lua_close(L);
+	return 0;
+}
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index 861e9e11b5..aabc833186 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -72,7 +72,9 @@ add_library(core STATIC ${common_sources})
 add_dependencies(core generate_headers luajit)
 set_target_properties(core PROPERTIES COMPILE_FLAGS "${core_cflags}")
 
-set (common_libraries cfg core ev coro gopt misc objc luajit)
+set (common_libraries cfg core ev coro gopt misc objc)
+set (common_libraries ${common_libraries} ${LUAJIT_LIB})
+
 if (TARGET_OS_LINUX)
   set (common_libraries ${common_libraries} dl)
 endif()
diff --git a/core/admin.m b/core/admin.m
index b403e23074..215ed19ef8 100644
--- a/core/admin.m
+++ b/core/admin.m
@@ -41,9 +41,10 @@
 #include TARANTOOL_CONFIG
 #include <tbuf.h>
 #include <util.h>
-#include "third_party/luajit/src/lua.h"
-#include "third_party/luajit/src/lauxlib.h"
-#include "third_party/luajit/src/lualib.h"
+
+#include LUAJIT_LUA_H
+#include LUAJIT_LAUXLIB_H
+#include LUAJIT_LUALIB_H
 
 static const char *help =
 	"available commands:" CRLF
@@ -60,7 +61,6 @@ static const char *help =
 	" - lua command" CRLF
 	" - reload configuration" CRLF;
 
-
 static const char *unknown_command = "unknown command. try typing help." CRLF;
 
 
diff --git a/core/admin.rl b/core/admin.rl
index 10d119709c..a11b770985 100644
--- a/core/admin.rl
+++ b/core/admin.rl
@@ -39,9 +39,10 @@
 #include TARANTOOL_CONFIG
 #include <tbuf.h>
 #include <util.h>
-#include "third_party/luajit/src/lua.h"
-#include "third_party/luajit/src/lauxlib.h"
-#include "third_party/luajit/src/lualib.h"
+
+#include LUAJIT_LUA_H
+#include LUAJIT_LAUXLIB_H
+#include LUAJIT_LUALIB_H
 
 static const char *help =
 	"available commands:" CRLF
@@ -58,7 +59,6 @@ static const char *help =
 	" - lua command" CRLF
 	" - reload configuration" CRLF;
 
-
 static const char *unknown_command = "unknown command. try typing help." CRLF;
 
 %%{
diff --git a/core/tarantool_lua.m b/core/tarantool_lua.m
index 04f6dcb3de..af752ee42f 100644
--- a/core/tarantool_lua.m
+++ b/core/tarantool_lua.m
@@ -29,10 +29,10 @@
  * SUCH DAMAGE.
  */
 #include "tarantool.h"
-/* use a full path to avoid clashes with system lua */
-#include "third_party/luajit/src/lua.h"
-#include "third_party/luajit/src/lauxlib.h"
-#include "third_party/luajit/src/lualib.h"
+
+#include LUAJIT_LUA_H
+#include LUAJIT_LAUXLIB_H
+#include LUAJIT_LUALIB_H
 
 #include "pickle.h"
 #include "fiber.h"
diff --git a/include/config.h.cmake b/include/config.h.cmake
index 80ae5a6c5d..8c0f8cd31d 100644
--- a/include/config.h.cmake
+++ b/include/config.h.cmake
@@ -50,8 +50,16 @@
  * Defined if this is a big-endian system.
  */
 #cmakedefine HAVE_BYTE_ORDER_BIG_ENDIAN 1
-
+/*
+ * predefined /etc directory prefix.
+ */
 #define SYSCONF_DIR "@CMAKE_SYSCONF_DIR@"
+/*
+ * path to luajit headers.
+ */
+#define LUAJIT_LUA_H     "@LUAJIT_INCLUDE@/lua.h"
+#define LUAJIT_LUALIB_H  "@LUAJIT_INCLUDE@/lualib.h"
+#define LUAJIT_LAUXLIB_H "@LUAJIT_INCLUDE@/lauxlib.h"
 /*
  * vim: syntax=c
  */
diff --git a/mod/box/box_lua.m b/mod/box/box_lua.m
index 0a5087d16e..4a957c57f7 100644
--- a/mod/box/box_lua.m
+++ b/mod/box/box_lua.m
@@ -31,10 +31,10 @@
 #include "box_lua.h"
 #include "tarantool.h"
 #include "box.h"
-/* use a full path to avoid clashes with system Lua */
-#include "third_party/luajit/src/lua.h"
-#include "third_party/luajit/src/lauxlib.h"
-#include "third_party/luajit/src/lualib.h"
+
+#include LUAJIT_LUA_H
+#include LUAJIT_LAUXLIB_H
+#include LUAJIT_LUALIB_H
 
 #include "pickle.h"
 #include "tuple.h"
diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt
index a6715612cc..9834c3c549 100644
--- a/third_party/CMakeLists.txt
+++ b/third_party/CMakeLists.txt
@@ -8,36 +8,47 @@ endif()
 add_subdirectory(coro)
 add_subdirectory(gopt)
 
-set (luajit_buildoptions BUILDMODE=static)
-if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")
-    set (luajit_buildoptions ${luajit_buildoptions} CCOPT=-O0)
-    set (luajit_buildoptions ${luajit_buildoptions} CCDEBUG=-ggdb)
-    set (luajit_buildoptions ${luajit_buildoptions} XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT')
-endif()
-set (luajit_buildoptions ${luajit_buildoptions} Q='')
-if (${PROJECT_BINARY_DIR} STREQUAL ${PROJECT_SOURCE_DIR})
-    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/third_party/luajit/src/libluajit.a
-        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/third_party/luajit
-        COMMAND make clean
-        COMMAND make -C src -t buildvm_x86.h buildvm_arm.h
-                        buildvm_x64.h buildvm_x64win.h
-        COMMAND make -C src ${luajit_buildoptions}
-        DEPENDS ${CMAKE_SOURCE_DIR}/CMakeCache.txt
-    )
-else()
-    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/third_party/luajit
-        COMMAND mkdir ${PROJECT_BINARY_DIR}/third_party/luajit
-    )
-    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/third_party/luajit/src/libluajit.a
-        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/third_party/luajit
-        COMMAND cp -r ${PROJECT_SOURCE_DIR}/third_party/luajit/* .
-        COMMAND make clean
-        COMMAND make -C src -t buildvm_x86.h buildvm_arm.h
-                        buildvm_x64.h buildvm_x64win.h
-        COMMAND make -C src ${luajit_buildoptions}
-        DEPENDS ${PROJECT_BINARY_DIR}/CMakeCache.txt ${PROJECT_BINARY_DIR}/third_party/luajit
+macro (luajit_build)
+    set (luajit_buildoptions BUILDMODE=static)
+    if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")
+        set (luajit_buildoptions ${luajit_buildoptions} CCOPT=-O0)
+        set (luajit_buildoptions ${luajit_buildoptions} CCDEBUG=-ggdb)
+        set (luajit_buildoptions ${luajit_buildoptions} XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT')
+    endif()
+    set (luajit_buildoptions ${luajit_buildoptions} Q='')
+    if (${PROJECT_BINARY_DIR} STREQUAL ${PROJECT_SOURCE_DIR})
+        add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/third_party/luajit/src/libluajit.a
+            WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/third_party/luajit
+            COMMAND make clean
+            COMMAND make -C src -t buildvm_x86.h buildvm_arm.h
+                            buildvm_x64.h buildvm_x64win.h
+            COMMAND make -C src ${luajit_buildoptions}
+            DEPENDS ${CMAKE_SOURCE_DIR}/CMakeCache.txt
+        )
+    else()
+        add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/third_party/luajit
+            COMMAND mkdir ${PROJECT_BINARY_DIR}/third_party/luajit
+        )
+        add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/third_party/luajit/src/libluajit.a
+            WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/third_party/luajit
+            COMMAND cp -r ${PROJECT_SOURCE_DIR}/third_party/luajit/* .
+            COMMAND make clean
+            COMMAND make -C src -t buildvm_x86.h buildvm_arm.h
+                            buildvm_x64.h buildvm_x64win.h
+            COMMAND make -C src ${luajit_buildoptions}
+            DEPENDS ${PROJECT_BINARY_DIR}/CMakeCache.txt ${PROJECT_BINARY_DIR}/third_party/luajit
+        )
+    endif()
+    add_custom_target(libluajit
+        DEPENDS ${PROJECT_BINARY_DIR}/third_party/luajit/src/libluajit.a
     )
+    unset (luajit_buildoptions)
+endmacro()
+
+#
+# building shipped luajit only if there is no
+# usable system one (see cmake/luajit.cmake) or by demand.
+#
+if (ENABLE_LUAJIT)
+    luajit_build()
 endif()
-add_custom_target(libluajit
-    DEPENDS ${PROJECT_BINARY_DIR}/third_party/luajit/src/libluajit.a
-)
-- 
GitLab