Skip to content

Commit

Permalink
Fix Unix compilation using CMake.
Browse files Browse the repository at this point in the history
Add platform detection that's compatible with the old buildsystem and its m4
autoconfigury.

Make Travis build and test using CMake as a separate target.
  • Loading branch information
qris committed Sep 2, 2016
1 parent 11a8c44 commit 462da98
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 33 deletions.
7 changes: 7 additions & 0 deletions .travis.yml
Expand Up @@ -12,7 +12,13 @@ sudo: false

addons:
apt:
# We need cmake > 2.8.7
# https://github.com/travis-ci/travis-ci/issues/4631#issuecomment-191153634
sources:
- george-edison55-precise-backports # cmake 3.2.3 / doxygen 1.8.3
packages:
- cmake
- cmake-data
- libdb-dev
- libreadline-dev
- libssl-dev
Expand All @@ -23,6 +29,7 @@ addons:
env:
- TEST_TARGET=debug
- TEST_TARGET=release
- BUILD=cmake

script:
- ./infrastructure/travis-build.sh
269 changes: 260 additions & 9 deletions infrastructure/cmake/CMakeLists.txt
Expand Up @@ -89,6 +89,7 @@ if(NOT status EQUAL 0)
"status ${status}: ${command_output}")
endif()

add_definitions(-DBOX_CMAKE -DNEED_BOX_VERSION_H)
if(WIN32)
add_definitions(-DWIN32)
endif()
Expand Down Expand Up @@ -324,17 +325,22 @@ endforeach()
# generators such as MSVC. We need to use a generator expression instead.
target_compile_definitions(lib_common PUBLIC $<$<CONFIG:Release>:-DBOX_RELEASE_BUILD>)

# Tell QDBM not to build itself as a DLL, because we want to link statically to it.
target_compile_definitions(qdbm PUBLIC -DQDBM_STATIC)
# Detect platform features and write BoxConfig.h.in. Reuse code from
# infrastructure/m4/boxbackup_tests.m4 where possible

# Silence some less-useful warnings
if(MSVC)
add_definitions(/wd4996 /wd4291)
# target_link_libraries(qdbm PRIVATE /IGNORE:LNK4006)
set_property(TARGET qdbm PROPERTY CMAKE_STATIC_LINKER_FLAGS /IGNORE:LNK4006)
endif(MSVC)
include(CheckIncludeFiles)
include(CheckFunctionExists)
include(CheckSymbolExists)
include(CheckLibraryExists)
include(CheckCXXSourceCompiles)

target_link_libraries(lib_common PUBLIC ws2_32 gdi32)
set(boxconfig_h_file "${CMAKE_BINARY_DIR}/BoxConfig.h.in")
file(REMOVE "${boxconfig_h_file}")
file(WRITE "${boxconfig_h_file}" "// Auto-generated by CMake. Do not edit.\n")

if(WIN32)
target_link_libraries(lib_common PUBLIC ws2_32 gdi32)
endif()

# Link to ZLib
# http://stackoverflow.com/a/6174604/648162
Expand Down Expand Up @@ -406,6 +412,251 @@ if(READLINE_FOUND)
target_link_libraries(lib_common PUBLIC ${Readline_LIBRARY})
endif()

set(boxconfig_cmake_h_dir "${base_dir}/lib/common")
# Get the values of all directories added to the INCLUDE_DIRECTORIES property
# by include_directory() statements, and save it in CMAKE_REQUIRED_INCLUDES
# which check_include_files() uses to set the include file search path:
get_property(CMAKE_REQUIRED_INCLUDES DIRECTORY PROPERTY INCLUDE_DIRECTORIES)
list(APPEND CMAKE_REQUIRED_INCLUDES "${boxconfig_cmake_h_dir}")
# message(STATUS "CMAKE_REQUIRED_INCLUDES=${CMAKE_REQUIRED_INCLUDES}")

# Save the original BoxConfig.cmake.h so that we can move it back later,
# and not need to recompile everything.
move_file_if_exists(
"${boxconfig_cmake_h_dir}/BoxConfig.cmake.h"
"${boxconfig_cmake_h_dir}/BoxConfig.cmake.h.bak")

foreach(m4_filename boxbackup_tests.m4 ax_check_mount_point.m4 ax_func_syscall.m4)
file(STRINGS "${base_dir}/infrastructure/m4/${m4_filename}" m4_functions REGEX "^ *AC[_A-Z]+\\(.*\\)$")
foreach(m4_function ${m4_functions})
if(DEBUG)
message(STATUS "Processing m4_function: ${m4_function}")
endif()

string(REGEX MATCH .* ac_check_headers ${m4_function})
if(m4_function MATCHES "^ *AC_CHECK_HEADERS?\\(\\[([a-z./ ]+)\\](.*)\\)$")
if(DEBUG)
message(STATUS "Processing ac_check_headers: ${CMAKE_MATCH_1}")
endif()

# http://stackoverflow.com/questions/5272781/what-is-common-way-to-split-string-into-list-with-cmake
string(REPLACE " " ";" header_files ${CMAKE_MATCH_1})

foreach(header_file ${header_files})
list(APPEND detect_header_files ${header_file})
endforeach()
elseif(m4_function MATCHES "^ *AC_CHECK_FUNCS\\(\\[([a-z./_ ]+)\\](.*)\\)$")
if(DEBUG)
message(STATUS "Processing ac_check_funcs: ${CMAKE_MATCH_1}")
endif()

# http://stackoverflow.com/questions/5272781/what-is-common-way-to-split-string-into-list-with-cmake
string(REPLACE " " ";" function_names ${CMAKE_MATCH_1})

foreach(function_name ${function_names})
list(APPEND detect_functions ${function_name})
endforeach()
elseif(m4_function MATCHES "^ *AC_CHECK_DECLS\\(\\[([A-Za-z._/ ]+)\\](,,, ..#include <([^>]+)>..)?\\)$")
if(DEBUG)
message(STATUS "Processing ac_check_decls: ${CMAKE_MATCH_1} in ${CMAKE_MATCH_3}")
endif()

# http://stackoverflow.com/questions/5272781/what-is-common-way-to-split-string-into-list-with-cmake
string(REPLACE " " ";" decl_names "${CMAKE_MATCH_1}")
string(REPLACE " " ";" header_files "${CMAKE_MATCH_3}")

foreach(decl_name ${decl_names})
string(TOUPPER ${decl_name} platform_var_name)
string(REGEX REPLACE "[/.]" "_" platform_var_name ${platform_var_name})
check_symbol_exists("${decl_name}" "${header_files}" HAVE_DECL_${platform_var_name})
file(APPEND "${boxconfig_h_file}" "#cmakedefine01 HAVE_DECL_${platform_var_name}\n")
endforeach()
elseif(m4_function MATCHES "^ *AC_SEARCH_LIBS\\(\\[([A-Za-z._/ ]+)\\], \\[([A-Za-z._]+)\\]\\)$")
if(DEBUG)
message(STATUS "Processing ac_search_libs: ${CMAKE_MATCH_1} in ${CMAKE_MATCH_2}")
endif()

set(function_name ${CMAKE_MATCH_1})
# http://stackoverflow.com/questions/5272781/what-is-common-way-to-split-string-into-list-with-cmake
string(REPLACE " " ";" library_names "${CMAKE_MATCH_2}")

foreach(library_name ${library_names})
string(TOUPPER ${library_name} platform_var_name)
check_library_exists(${library_name} ${function_name} "" HAVE_LIB_${platform_var_name})
if(HAVE_LIB_${platform_var_name})
target_link_libraries(lib_common PUBLIC ${library_name})
endif()
endforeach()
elseif(m4_function MATCHES "^ *AC_CHECK_MEMBERS\\(\\[([A-Za-z._/ ]+)\\.([[A-Za-z_]+)\\](,,, ..(#include <([^>]+)>)..)?\\)$")
if(DEBUG)
message(STATUS "Processing ac_check_members: ${CMAKE_MATCH_1}.${CMAKE_MATCH_2} in ${CMAKE_MATCH_5}")
endif()

set(struct_name "${CMAKE_MATCH_1}")
set(member_name "${CMAKE_MATCH_2}")
set(include_file "${CMAKE_MATCH_5}")

string(TOUPPER "${struct_name}_${member_name}" platform_var_name)
string(REGEX REPLACE "[/. ]" "_" platform_var_name ${platform_var_name})

CHECK_CXX_SOURCE_COMPILES([=[
#include "BoxConfig.cmake.h"
#include <${include_file}>
int main()
{
${struct_name} foo;
return sizeof(foo.${member_name}) > 0 ? 0 : 1;
}
]=] "HAVE_${platform_var_name}")
file(APPEND "${boxconfig_h_file}" "#cmakedefine HAVE_${platform_var_name}\n")
endif()
endforeach()

# Build an intermediate version of BoxConfig.cmake.h for use in the following tests.
configure_file("${boxconfig_h_file}" "${boxconfig_cmake_h_dir}/BoxConfig.cmake.h")
endforeach()

list(APPEND detect_header_files mntent.h sys/mnttab.h sys/mount.h sys/param.h)

foreach(header_file ${detect_header_files})
list(APPEND detect_header_files ${header_file})
string(TOUPPER ${header_file} platform_var_name)
string(REGEX REPLACE "[/.]" "_" platform_var_name ${platform_var_name})
check_include_files(${header_file} HAVE_${platform_var_name})
file(APPEND "${boxconfig_h_file}" "#cmakedefine HAVE_${platform_var_name}\n")
endforeach()

if(NOT HAVE_PCREPOSIX_H)
message(FATAL_ERROR "pcreposix.h not found at PCRE_ROOT/include: ${PCRE_ROOT}/include")
endif()

# PCRE is required, so unconditionally define this:
set(HAVE_REGEX_SUPPORT 1)
file(APPEND "${boxconfig_h_file}" "#cmakedefine HAVE_REGEX_SUPPORT\n")

foreach(function_name ${detect_functions})
string(TOUPPER ${function_name} platform_var_name)
string(REGEX REPLACE "[/.]" "_" platform_var_name ${platform_var_name})
check_function_exists(${function_name} HAVE_${platform_var_name})
file(APPEND "${boxconfig_h_file}" "#cmakedefine HAVE_${platform_var_name}\n")
endforeach()

check_symbol_exists(dirfd "dirent.h" HAVE_DECL_DIRFD)
file(APPEND "${boxconfig_h_file}" "#cmakedefine01 HAVE_DECL_DIRFD\n")

# Emulate ax_check_mount_point.m4
# These checks are run by multi-line M4 commands which are harder to parse/fake using
# regexps above, so we hard-code them here:
CHECK_CXX_SOURCE_COMPILES([=[
#include "BoxConfig.cmake.h"
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
#include <sys/mount.h>
int main()
{
struct statfs foo;
return sizeof(foo.f_mntonname) > 0 ? 0 : 1;
}
]=] "HAVE_STRUCT_STATFS_F_MNTONNAME")
file(APPEND "${boxconfig_h_file}" "#cmakedefine HAVE_STRUCT_STATFS_F_MNTONNAME\n")
CHECK_CXX_SOURCE_COMPILES([=[
#include "BoxConfig.cmake.h"
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
#include <sys/mount.h>
int main()
{
struct statvfs foo;
return sizeof(foo.f_mntonname) > 0 ? 0 : 1;
}
]=] "HAVE_STRUCT_STATVFS_F_MNTONNAME")
file(APPEND "${boxconfig_h_file}" "#cmakedefine HAVE_STRUCT_STATVFS_F_MNTONNAME\n")
if(HAVE_STRUCT_STATFS_F_MNTONNAME OR
HAVE_STRUCT_STATVFS_F_MNTONNAME OR
HAVE_STRUCT_MNTENT_MNT_DIR OR
HAVE_STRUCT_MNTTAB_MNT_MOUNTP)

set(HAVE_MOUNTS 1)
file(APPEND "${boxconfig_h_file}" "#cmakedefine HAVE_MOUNTS\n")
endif()

# Emulate ax_random_device.m4
if(EXISTS /dev/urandom)
set(RANDOM_DEVICE /dev/urandom)
elseif(EXISTS /dev/arandom)
set(RANDOM_DEVICE /dev/arandom)
elseif(EXISTS /dev/random)
set(RANDOM_DEVICE /dev/random)
endif()
if(RANDOM_DEVICE)
set(HAVE_RANDOM_DEVICE TRUE)
endif()
file(APPEND "${boxconfig_h_file}" "#cmakedefine RANDOM_DEVICE \"${RANDOM_DEVICE}\"\n")
file(APPEND "${boxconfig_h_file}" "#cmakedefine HAVE_RANDOM_DEVICE\n")

# Build an intermediate version of BoxConfig.cmake.h for use in the following tests:
configure_file("${boxconfig_h_file}" "${boxconfig_cmake_h_dir}/BoxConfig.cmake.h")

foreach(struct_member_name "struct ucred.uid" "struct ucred.cr_uid")
string(REGEX MATCH "(.*)\\.(.*)" dummy_var ${struct_member_name})
set(struct_name "${CMAKE_MATCH_1}")
set(member_name "${CMAKE_MATCH_2}")

string(TOUPPER "${struct_name}_${member_name}" platform_var_name)
string(REGEX REPLACE "[/. ]" "_" platform_var_name ${platform_var_name})

CHECK_CXX_SOURCE_COMPILES([=[
#include "BoxConfig.cmake.h"

#ifdef HAVE_UCRED_H
# include <ucred.h>
#endif

#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif

#ifdef HAVE_SYS_UCRED_H
# include <sys/ucred.h>
#endif

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif

int main()
{
${struct_name} foo;
return sizeof(foo.${member_name}) > 0 ? 0 : 1;
}
]=] "HAVE_${platform_var_name}")
file(APPEND "${boxconfig_h_file}" "#cmakedefine HAVE_${platform_var_name}\n")
endforeach()
set(CMAKE_REQUIRED_INCLUDES "")

# Build the final version of BoxConfig.cmake.h, as a temporary file.
configure_file("${boxconfig_h_file}" "${boxconfig_cmake_h_dir}/BoxConfig.cmake.h.new")

# Move the original back into place, and then replace it with the
# temporary one if different (which will force a rebuild of everything).
move_file_if_exists(
"${boxconfig_cmake_h_dir}/BoxConfig.cmake.h.bak"
"${boxconfig_cmake_h_dir}/BoxConfig.cmake.h")
replace_file_if_different(
"${boxconfig_cmake_h_dir}/BoxConfig.cmake.h"
"${boxconfig_cmake_h_dir}/BoxConfig.cmake.h.new")

# Tell QDBM not to build itself as a DLL, because we want to link statically to it.
target_compile_definitions(qdbm PUBLIC -DQDBM_STATIC)

# Silence some less-useful warnings
if(MSVC)
add_definitions(/wd4996 /wd4291)
set_property(TARGET qdbm PROPERTY CMAKE_STATIC_LINKER_FLAGS /IGNORE:LNK4006)
endif(MSVC)

# Define the location of the Perl executable, needed by testbackupstorefix
file(TO_NATIVE_PATH "${PERL_EXECUTABLE}" perl_executable_native)
string(REPLACE "\\" "\\\\" perl_path_escaped ${perl_executable_native})
Expand Down
30 changes: 19 additions & 11 deletions infrastructure/travis-build.sh
Expand Up @@ -5,17 +5,25 @@ set -x

ccache -s

cd `dirname $0`/..
./bootstrap
./configure CC="ccache $CC" CXX="ccache $CXX" "$@"
grep CXX config.status
make V=1
./runtest.pl ALL $TEST_TARGET
# When making a release build, also check that we can build the default
# target and "parcels" (which is the same thing):
if [ "$TEST_TARGET" = "release" ]; then
make
make parcels
if [ "$BUILD" = 'cmake' ]; then
cd `dirname $0`
mkdir -p cmake/build
cd cmake/build
cmake --version
cmake -DCMAKE_BUILD_TYPE:STRING=$TEST_TARGET ..
make install
[ "$TEST" = "n" ] || ctest -V
else
cd `dirname $0`/..
./bootstrap
./configure CC="ccache $CC" CXX="ccache $CXX" "$@"
grep CXX config.status
make V=1
./runtest.pl ALL $TEST_TARGET
if [ "$TEST_TARGET" = "release" ]; then
make
make parcels
fi
fi

ccache -s
2 changes: 1 addition & 1 deletion lib/bbackupquery/makedocumentation.pl.in
Expand Up @@ -3,7 +3,7 @@ use strict;

print "Creating built-in documentation for bbackupquery...\n";

open DOC,"Documentation.txt" or die "Can't open Documentation.txt file: $!";
open DOC, "Documentation.txt" or die "Can't open Documentation.txt file: $!";
my $section;
my %help;
my @in_order;
Expand Down
10 changes: 6 additions & 4 deletions lib/common/BoxPlatform.h
Expand Up @@ -21,11 +21,13 @@

#define PLATFORM_DEV_NULL "/dev/null"

#ifdef _MSC_VER
#include "BoxConfig-MSVC.h"
#define NEED_BOX_VERSION_H
#if defined BOX_CMAKE
# include "BoxConfig.cmake.h"
#elif defined _MSC_VER
# include "BoxConfig-MSVC.h"
# define NEED_BOX_VERSION_H
#else
#include "BoxConfig.h"
# include "BoxConfig.h"
#endif

#ifdef WIN32
Expand Down
16 changes: 9 additions & 7 deletions lib/common/Test.h
Expand Up @@ -243,12 +243,14 @@ void safe_sleep(int seconds);
std::auto_ptr<Configuration> load_config_file(const std::string& config_file,
const ConfigurationVerify& verify);

#ifdef _MSC_VER
// Our CMakeFiles compile tests to different executable filenames,
// e.g. test_common.exe instead of _test.exe.
#define TEST_EXECUTABLE BOX_MODULE ".exe"
#else
#define TEST_EXECUTABLE "./_test"
#endif
#ifndef TEST_EXECUTABLE
# ifdef _MSC_VER
// Our CMakeFiles compile tests to different executable filenames,
// e.g. test_common.exe instead of _test.exe.
#define TEST_EXECUTABLE BOX_MODULE ".exe"
# else
#define TEST_EXECUTABLE "./_test"
# endif
#endif // TEST_EXECUTABLE

#endif // TEST__H

0 comments on commit 462da98

Please sign in to comment.