From 3e03b078319238d87358fe9c9ba156e29072b446 Mon Sep 17 00:00:00 2001 From: Paul Harris Date: Mon, 4 Jun 2012 01:27:48 +0800 Subject: [PATCH] Add CMake build system. Added multiple CMake-related files to project. Supports building the library and the tests. See CMakeLists.txt for notes on how it works. I had to adjust 3 existing files in order to disable some configuration that should be taken care of by cmake/automake anyway. I also added jansson.def from a future jansson version, to test cmake's support for .def files (which works fine). --- CMakeLists.txt | 276 +++++++++++++++++++++++ CMakeModules/CheckFunctionKeywords.cmake | 15 ++ CMakeModules/FindSphinx.cmake | 16 ++ config.h.cmake | 22 ++ src/CMakeLists.txt | 23 ++ src/jansson.h | 2 + src/jansson_config.h.cmake | 62 +++++ src/load.c | 2 + src/strconv.c | 5 + test/suites/api/CMakeLists.txt | 31 +++ 10 files changed, 454 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 CMakeModules/CheckFunctionKeywords.cmake create mode 100644 CMakeModules/FindSphinx.cmake create mode 100644 config.h.cmake create mode 100644 src/CMakeLists.txt create mode 100644 src/jansson_config.h.cmake create mode 100644 test/suites/api/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..696b276 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,276 @@ +# Notes: +# +# Author: Paul Harris, June 2012 +# +# Supports: building static/shared, release/debug/etc, can also build html docs and some of the tests. +# Note that its designed for out-of-tree builds, so it will not pollute your source tree. +# +# TODO 1: Finish implementing tests. api tests are working, but the valgrind variants are not flagging problems. +# +# TODO 2: There is a check_exports script that would try and incorporate. +# +# TODO 3: Consolidate version numbers, currently the version number is written into: * cmake (here) * autotools (the configure) * source code header files. Should not be written directly into header files, autotools/cmake can do that job. +# +# Brief intro on how to use cmake: +# > mkdir build (somewhere - we do out-of-tree builds) +# > use cmake, ccmake, or cmake-gui to configure the project. for linux, you can only choose one variant: release,debug,etc... and static or shared. +# >> example: +# >> cd build +# >> ccmake -i ../path_to_jansson_dir +# >> inside, configure your options. press C until there are no lines with * next to them. +# >> note, I like to configure the 'install' path to ../install, so I get self-contained clean installs I can point other projects to. +# >> press G to 'generate' the project files. +# >> make (to build the project) +# >> make install +# >> make test (to run the tests, if you enabled them) +# +# Brief description on how it works: +# There is a small heirachy of CMakeLists.txt files which define how the project is built. +# Header file detection etc is done, and the results are written into config.h and jansson_config.h, +# which are generated from the corresponding config.h.cmake and jansson_config.h.cmake template files. +# The generated header files end up in the build directory - not in the source directory. +# The rest is down to the usual make process. + + + +cmake_minimum_required (VERSION 2.8) +# required for exports? cmake_minimum_required (VERSION 2.8.6) +project (jansson C) + +# Options +OPTION (BUILD_SHARED_LIBS "Build shared libraries." OFF) + +# This is how I thought it should go +# set (JANSSON_VERSION "2.3.1") +# set (JANSSON_SOVERSION 2) + +# This is what is required to match the same numbers as automake's +set (JANSSON_VERSION "4.3.1") +set (JANSSON_SOVERSION 4) + +# for CheckFunctionKeywords +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") + +include (CheckFunctionExists) +include (CheckFunctionKeywords) +include (CheckIncludeFiles) +include (CheckTypeSize) + + +# Check for the int-type includes +check_include_files (sys/types.h HAVE_SYS_TYPES_H) +check_include_files (inttypes.h HAVE_INTTYPES_H) +check_include_files (stdint.h HAVE_STDINT_H) + + +# Check our 64 bit integer sizes +check_type_size (__int64 __INT64) +check_type_size (int64_t INT64_T) +check_type_size ("long long" LONG_LONG_INT) + +# Check our 32 bit integer sizes +check_type_size (int32_t INT32_T) +check_type_size (__int32 __INT32) +check_type_size ("long" LONG_INT) +check_type_size ("int" INT) + +if (HAVE___INT32) + set (JSON_INT32 __int32) +elseif (HAVE_LONG AND (${LONG_INT} EQUAL 4)) + set (JSON_INT32 long) +elseif (HAVE_INT AND (${INT} EQUAL 4)) + set (JSON_INT32 int) +elseif (HAVE_INT32_T) + set (JSON_INT32 int32_t) +else () + message (FATAL_ERROR "Could not detect a valid 32 bit integer type") +endif () + +# Check if we have int32_t, if not then we will use long (in config.h.cmake) +check_type_size (int32_t INT32_T) + + +# Check for all the variants of strtoll +check_function_exists (strtoll HAVE_STRTOLL) +check_function_exists (strtoq HAVE_STRTOQ) +check_function_exists (_strtoi64 HAVE__STRTOI64) + +# Figure out what variant we should use +if (HAVE_STRTOLL) + set (JSON_STRTOINT strtoll) +elseif (HAVE_STRTOQ) + set (JSON_STRTOINT strtoq) +elseif (HAVE__STRTOI64) + set (JSON_STRTOINT _strtoi64) +else () + # fallback to strtol (32 bit) + # this will set all the required variables + set (JSON_STRTOINT strtol) + set (JSON_INT_T long) + set (JSON_INTEGER_FORMAT "\"ld\"") +endif () + +# if we haven't defined JSON_INT_T, then we have a 64 bit conversion function. +# detect what to use for the 64 bit type. +# Note: I will prefer long long if I can get it, as that is what the automake system aimed for. +if (NOT DEFINED JSON_INT_T) + if (HAVE_LONG_LONG_INT AND (${LONG_LONG_INT} EQUAL 8)) + set (JSON_INT_T "long long") + elseif (HAVE_INT64_T) + set (JSON_INT_T int64_t) + elseif (HAVE___INT64) + set (JSON_INT_T __int64) + else () + message (FATAL_ERROR "Could not detect 64 bit type, although I detected the strtoll equivalent") + endif () + + # Apparently, Borland BCC and MSVC wants I64d, + # Borland BCC could also accept LD + # and gcc wants ldd, + # I am not sure what cygwin will want, so I will assume I64d + + if (WIN32) # matches both msvc and cygwin + set (JSON_INTEGER_FORMAT "\"I64d\"") + else () + set (JSON_INTEGER_FORMAT "\"lld\"") + endif () +endif () + + +# If locale.h and localeconv() are available, define to 1, otherwise to 0. +check_include_files (locale.h HAVE_LOCALE_H) +check_function_exists (localeconv HAVE_LOCALECONV) + +if (HAVE_LOCALECONV AND HAVE_LOCALE_H) + set (JSON_HAVE_LOCALECONV 1) +else () + set (JSON_HAVE_LOCALECONV 0) +endif () + + +# check if we have setlocale +check_function_exists (setlocale HAVE_SETLOCALE) + + +# Check what the inline keyword is. +# Note that the original JSON_INLINE was always set to just 'inline', so this goes further. +check_function_keywords("inline") +check_function_keywords("__inline") +check_function_keywords("__inline__") +# check_function_keywords("__declspec(dllexport)") +# check_function_keywords("__declspec(dllimport)") + +if (HAVE_INLINE) + set (JSON_INLINE inline) +elseif (HAVE___INLINE) + set (JSON_INLINE __inline) +elseif (HAVE___INLINE__) + set (JSON_INLINE __inline__) +else (HAVE_INLINE) + # no inline on this platform + set (JSON_INLINE) +endif (HAVE_INLINE) + +# Find our snprintf +check_function_exists (snprintf HAVE_SNPRINTF) +# check_function_exists (_snprintf_s HAVE__SNPRINTF_s) +check_function_exists (_snprintf HAVE__SNPRINTF) + +if (HAVE_SNPRINTF) + set (JSON_SNPRINTF snprintf) +# elseif (HAVE__SNPRINTF_s) + # set (JSON_SNPRINTF _snprintf_s) +elseif (HAVE__SNPRINTF) + set (JSON_SNPRINTF _snprintf) +endif () + +if (MSVC) + set (CMAKE_DEBUG_POSTFIX "_d") +endif (MSVC) + +# configure the public config file +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/src/jansson_config.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h) + +# Copy the jansson.h file to the public include folder +file (COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/) + + +# configure the private config file +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/private_include/config.h) + +# and tell the source code to include it +add_definitions (-DHAVE_CONFIG_H) + +include_directories (${CMAKE_CURRENT_BINARY_DIR}/include) +include_directories (${CMAKE_CURRENT_BINARY_DIR}/private_include) + +add_subdirectory (src) + +install (FILES ${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h DESTINATION include) +install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h DESTINATION include) + +# For building Documentation (uses Sphinx) +OPTION (BUILD_DOCS "Build documentation (uses python-sphinx)." OFF) +if (BUILD_DOCS) + find_package(Sphinx REQUIRED) + + # configured documentation tools and intermediate build results + set(BINARY_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/_build") + + # Sphinx cache with pickled ReST documents + set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees") + + # HTML output directory + set(SPHINX_HTML_DIR "${CMAKE_CURRENT_BINARY_DIR}/html") + + # CMake could be used to build the conf.py file too, + # eg it could automatically write the version of the program or change the theme. + # if(NOT DEFINED SPHINX_THEME) + # set(SPHINX_THEME default) + # endif() + # + # if(NOT DEFINED SPHINX_THEME_DIR) + # set(SPHINX_THEME_DIR) + # endif() + # + # configure_file( + # "${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in" + # "${BINARY_BUILD_DIR}/conf.py" + # @ONLY) + + add_custom_target(jansson_docs ALL + ${SPHINX_EXECUTABLE} + # -q # Enable for quiet mode + -b html + -d "${SPHINX_CACHE_DIR}" + # -c "${BINARY_BUILD_DIR}" # enable if using cmake-generated conf.py + "${CMAKE_CURRENT_SOURCE_DIR}/doc" + "${SPHINX_HTML_DIR}" + COMMENT "Building HTML documentation with Sphinx") + + message (STATUS "Documentation has been built in ${SPHINX_HTML_DIR}") +endif () + + +OPTION (BUILD_TESTS "Build tests ('make test' to execute tests)" OFF) + +if (BUILD_TESTS) + OPTION (TEST_WITH_VALGRIND "Enable valgrind tests (TODO flag when something is wrong, currently will always pass)" OFF) + + ENABLE_TESTING() + + if (TEST_WITH_VALGRIND) + # enable valgrind + set (CMAKE_MEMORYCHECK_COMMAND /usr/bin/valgrind) + set (CMAKE_MEMORYCHECK_COMMAND_OPTIONS "--leak-check=full --show-reachable=yes --track-origins=yes -q") + + set(MEMCHECK_COMMAND "${CMAKE_MEMORYCHECK_COMMAND} ${CMAKE_MEMORYCHECK_COMMAND_OPTIONS}") + separate_arguments(MEMCHECK_COMMAND) + endif () + + add_subdirectory (test/suites/api) +endif () + diff --git a/CMakeModules/CheckFunctionKeywords.cmake b/CMakeModules/CheckFunctionKeywords.cmake new file mode 100644 index 0000000..44601fd --- /dev/null +++ b/CMakeModules/CheckFunctionKeywords.cmake @@ -0,0 +1,15 @@ +include(CheckCSourceCompiles) + +macro(check_function_keywords _wordlist) + set(${_result} "") + foreach(flag ${_wordlist}) + string(REGEX REPLACE "[-+/ ()]" "_" flagname "${flag}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${flagname}") + check_c_source_compiles("${flag} void func(); void func() { } int main() { func(); return 0; }" ${have_flag}) + if(${have_flag} AND NOT ${_result}) + set(${_result} "${flag}") +# break() + endif(${have_flag} AND NOT ${_result}) + endforeach(flag) +endmacro(check_function_keywords) diff --git a/CMakeModules/FindSphinx.cmake b/CMakeModules/FindSphinx.cmake new file mode 100644 index 0000000..76f6e2a --- /dev/null +++ b/CMakeModules/FindSphinx.cmake @@ -0,0 +1,16 @@ +find_program(SPHINX_EXECUTABLE NAMES sphinx-build + HINTS + $ENV{SPHINX_DIR} + PATH_SUFFIXES bin + DOC "Sphinx documentation generator" +) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Sphinx DEFAULT_MSG + SPHINX_EXECUTABLE +) + +mark_as_advanced( +  SPHINX_EXECUTABLE +) diff --git a/config.h.cmake b/config.h.cmake new file mode 100644 index 0000000..c4e5a33 --- /dev/null +++ b/config.h.cmake @@ -0,0 +1,22 @@ +/* Reduced down to the defines that are actually used in the code */ + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LOCALE_H 1 + +/* Define to 1 if you have the 'setlocale' function. */ +#cmakedefine HAVE_SETLOCALE 1 + +/* Define to the type of a signed integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#cmakedefine HAVE_INT32_T 1 + +#ifndef HAVE_INT32_T +# define int32_t @JSON_INT32@ +#endif + +#ifndef HAVE_SNPRINTF +# define snprintf @JSON_SNPRINTF@ +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..8ac6bce --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,23 @@ +file (GLOB C_FILES *.c) + +if (BUILD_SHARED_LIBS) + + add_library (jansson SHARED ${C_FILES} jansson.def) + + set_target_properties (jansson PROPERTIES + VERSION ${JANSSON_VERSION} + SOVERSION ${JANSSON_SOVERSION}) + +else () + + add_library (jansson ${C_FILES}) + +endif () + +# LIBRARY for linux +# RUNTIME for windows (when building shared) +install (TARGETS jansson + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) diff --git a/src/jansson.h b/src/jansson.h index ec384d8..e53a301 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -52,6 +52,7 @@ typedef struct json_t { size_t refcount; } json_t; +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ #if JSON_INTEGER_IS_LONG_LONG #ifdef _WIN32 #define JSON_INTEGER_FORMAT "I64d" @@ -63,6 +64,7 @@ typedef long long json_int_t; #define JSON_INTEGER_FORMAT "ld" typedef long json_int_t; #endif /* JSON_INTEGER_IS_LONG_LONG */ +#endif #define json_typeof(json) ((json)->type) #define json_is_object(json) (json && json_typeof(json) == JSON_OBJECT) diff --git a/src/jansson_config.h.cmake b/src/jansson_config.h.cmake new file mode 100644 index 0000000..335ccc4 --- /dev/null +++ b/src/jansson_config.h.cmake @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2010-2012 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * + * This file specifies a part of the site-specific configuration for + * Jansson, namely those things that affect the public API in + * jansson.h. + * + * The CMake system will generate the jansson_config.h file and + * copy it to the build and install directories. + */ + +#ifndef JANSSON_CONFIG_H +#define JANSSON_CONFIG_H + +/* Define this so that we can disable scattered automake configuration in source files */ +#define JANSSON_USING_CMAKE + +/* Note: when using cmake, JSON_INTEGER_IS_LONG_LONG is not defined nor used, + * as we will also check for __int64 etc types. + * (the definition was used in the automake system) */ + +/* Bring in the cmake-detected defines */ +#cmakedefine HAVE_STDINT_H 1 +#cmakedefine HAVE_INTTYPES_H 1 +#cmakedefine HAVE_SYS_TYPES_H 1 + +/* Include our standard type header for the integer typedef */ + +#if defined(HAVE_STDINT_H) +# include +#elif defined(HAVE_INTTYPES_H) +# include +#elif defined(HAVE_SYS_TYPES_H) +# include +#endif + + +/* If your compiler supports the inline keyword in C, JSON_INLINE is + defined to `inline', otherwise empty. In C++, the inline is always + supported. */ +#ifdef __cplusplus +#define JSON_INLINE inline +#else +#define JSON_INLINE @JSON_INLINE@ +#endif + + +#define json_int_t @JSON_INT_T@ +#define json_strtoint @JSON_STRTOINT@ +#define JSON_INTEGER_FORMAT @JSON_INTEGER_FORMAT@ + + +/* If locale.h and localeconv() are available, define to 1, otherwise to 0. */ +#define JSON_HAVE_LOCALECONV @JSON_HAVE_LOCALECONV@ + + + +#endif diff --git a/src/load.c b/src/load.c index 16ff53d..2cc7a5f 100644 --- a/src/load.c +++ b/src/load.c @@ -446,6 +446,7 @@ out: jsonp_free(lex->value.string); } +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ #if JSON_INTEGER_IS_LONG_LONG #ifdef _MSC_VER /* Microsoft Visual Studio */ #define json_strtoint _strtoi64 @@ -455,6 +456,7 @@ out: #else #define json_strtoint strtol #endif +#endif static int lex_scan_number(lex_t *lex, int c, json_error_t *error) { diff --git a/src/strconv.c b/src/strconv.c index caa9ab8..3e2cb7c 100644 --- a/src/strconv.c +++ b/src/strconv.c @@ -5,6 +5,11 @@ #include "jansson_private.h" #include "strbuffer.h" +/* need config.h to get the correct snprintf */ +#ifdef HAVE_CONFIG_H +#include +#endif + #if JSON_HAVE_LOCALECONV #include diff --git a/test/suites/api/CMakeLists.txt b/test/suites/api/CMakeLists.txt new file mode 100644 index 0000000..a94d59e --- /dev/null +++ b/test/suites/api/CMakeLists.txt @@ -0,0 +1,31 @@ +set (tests + test_array + test_copy + test_dump + test_dump_callback + test_equal + test_load + test_loadb + test_memory_funcs + test_number + test_object + test_pack + test_simple + test_unpack) + +foreach (test ${tests}) + add_executable (${test} ${test}.c) + target_link_libraries (${test} jansson) +endforeach() + +# batch plain tests +foreach (test ${tests}) + add_test (${test} ${CMAKE_CURRENT_BINARY_DIR}/${test}) +endforeach() + +# batch valgrind tests +if (TEST_WITH_VALGRIND) + foreach (test ${tests}) + add_test (memcheck_${test} ${MEMCHECK_COMMAND} ${CMAKE_CURRENT_BINARY_DIR}/${test}) + endforeach() +endif ()