summaryrefslogtreecommitdiff
path: root/infrastructure/cmake
diff options
context:
space:
mode:
authorChris Wilson <qris@users.noreply.github.com>2016-10-01 20:44:48 +0100
committerGitHub <noreply@github.com>2016-10-01 20:44:48 +0100
commit80adfe939a663af4e279652f5404429f5594826c (patch)
treef316cd8fe3b837531055f702e79f84b49d8dc654 /infrastructure/cmake
parent8718a10d3b457e80a922d0f2ee6b02e686fb452d (diff)
parent65a8cf6577d36ad419f7313800a6fbaf93aaa097 (diff)
Merge rewritten build system.
Add a new CMake build system, based on the one used to build for AppVeyor (Windows) and made to work with Linux and fully tested by Travis CI. Rewrite the old autoconf-based Makefile generator build system to solve performance and correctness problems caused by highly parallelised recursive Make, which was causing Travis builds to fail. Now uses a single monolithic generated Makefile instead of recursive Make. Should also be faster, both for initial builds (with parallelism) and for rebuilds. Remove the old outdated Visual Studio project files, since it should now be possible to generate these with CMake. Add a CMake project that builds Box Backup and its dependencies, particularly useful for getting started as a developer on Windows.
Diffstat (limited to 'infrastructure/cmake')
-rw-r--r--infrastructure/cmake/CMakeLists.txt544
-rw-r--r--infrastructure/cmake/FindReadline.cmake84
-rwxr-xr-xinfrastructure/cmake/getversion.pl13
-rwxr-xr-xinfrastructure/cmake/msvc/bin_bbackupd.vcxproj.user8
-rwxr-xr-xinfrastructure/cmake/msvc/bin_bbstored.vcxproj.user8
-rwxr-xr-xinfrastructure/cmake/msvc/test_backupstore.vcxproj.user9
-rwxr-xr-xinfrastructure/cmake/msvc/test_backupstorefix.vcxproj.user7
-rwxr-xr-xinfrastructure/cmake/msvc/test_bbackupd.vcxproj.user8
-rwxr-xr-xinfrastructure/cmake/msvc/test_common.vcxproj.user7
-rwxr-xr-xinfrastructure/cmake/msvc/test_httpserver.vcxproj.user7
-rwxr-xr-xinfrastructure/cmake/msvc/test_raidfile.vcxproj.user7
-rw-r--r--infrastructure/cmake/windows/CMakeLists.txt89
12 files changed, 713 insertions, 78 deletions
diff --git a/infrastructure/cmake/CMakeLists.txt b/infrastructure/cmake/CMakeLists.txt
index a9bd549f..22c21553 100644
--- a/infrastructure/cmake/CMakeLists.txt
+++ b/infrastructure/cmake/CMakeLists.txt
@@ -1,17 +1,16 @@
cmake_minimum_required(VERSION 2.6)
+
+find_program(CCACHE_PROGRAM ccache)
+if(CCACHE_PROGRAM)
+ # Support Unix Makefiles and Ninja
+ set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
+endif()
+
project(BoxBackup)
enable_testing()
set(base_dir ${CMAKE_SOURCE_DIR}/../..)
-macro(cmake_to_native_path cmake_path native_path)
- if(WIN32)
- string(REPLACE "/" "\\" ${native_path} "${cmake_path}")
- else()
- set(${native_path} "${cmake_path}")
- endif()
-endmacro()
-
set(files_to_configure
bin/bbackupd/bbackupd-config
bin/bbstored/bbstored-certs
@@ -31,7 +30,6 @@ set(files_to_configure
contrib/windows/installer/boxbackup.mpi
infrastructure/BoxPlatform.pm
infrastructure/makebuildenv.pl
- infrastructure/makeparcels.pl
infrastructure/makedistribution.pl
lib/bbackupquery/makedocumentation.pl
lib/common/BoxPortsAndFiles.h
@@ -56,23 +54,54 @@ set(files_to_configure
include(FindPerl)
set(TARGET_PERL ${PERL_EXECUTABLE})
+function(replace_file_if_different dest_file source_file)
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" -E
+ copy_if_different "${source_file}" "${dest_file}")
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" -E
+ remove "${source_file}")
+endfunction()
+
+function(move_file_if_exists source_file dest_file)
+ if(EXISTS "${source_file}")
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" -E
+ rename "${source_file}" "${dest_file}")
+ endif()
+endfunction()
+
foreach(file_to_configure ${files_to_configure})
- configure_file("${base_dir}/${file_to_configure}.in" "${base_dir}/${file_to_configure}" @ONLY)
+ configure_file("${base_dir}/${file_to_configure}.in" "${base_dir}/${file_to_configure}.out" @ONLY)
+ replace_file_if_different(
+ "${base_dir}/${file_to_configure}"
+ "${base_dir}/${file_to_configure}.out")
endforeach()
-file(READ "${base_dir}/infrastructure/buildenv-testmain-template.cpp" test_template)
-
-
+# Work out the current Box version (requires Perl) and update lib/common/BoxVersion.h,
+# but only if it has changed, to avoid unnecessary complete rebuilds due to timestamps.
execute_process(
- COMMAND ${PERL_EXECUTABLE} ${base_dir}/infrastructure/msvc/getversion.pl
+ COMMAND ${PERL_EXECUTABLE} ${base_dir}/infrastructure/cmake/getversion.pl
+ WORKING_DIRECTORY ${base_dir}/infrastructure
RESULT_VARIABLE status
- OUTPUT_VARIABLE command_output
+ OUTPUT_VARIABLE new_version_define
ERROR_VARIABLE command_output)
if(NOT status EQUAL 0)
message(FATAL_ERROR "Failed to execute: "
- "${PERL_EXECUTABLE} ${base_dir}/infrastructure/msvc/getversion.pl: "
+ "${PERL_EXECUTABLE} ${base_dir}/infrastructure/cmake/getversion.pl: "
"status ${status}: ${command_output}")
endif()
+file(WRITE "${base_dir}/lib/common/BoxVersion.h.new" "${new_version_define}")
+replace_file_if_different(
+ "${base_dir}/lib/common/BoxVersion.h"
+ "${base_dir}/lib/common/BoxVersion.h.new")
+
+add_definitions(-DBOX_CMAKE -DNEED_BOX_VERSION_H)
+if(WIN32)
+ add_definitions(-DWIN32)
+endif()
+
+file(READ "${base_dir}/infrastructure/buildenv-testmain-template.cpp" test_template)
# Parsing Makefile.extra files in CMake script is a pain, so the relevant rules for
# code-generating Perl scripts are hard-coded here.
@@ -107,8 +136,8 @@ foreach(exception_file ${exception_files})
endforeach()
set(protocol_files
- lib/backupstore/backupprotocol.txt
- test/basicserver/testprotocol.txt
+ lib/backupstore/BackupProtocol.txt
+ test/basicserver/TestProtocol.txt
)
foreach(protocol_file ${protocol_files})
@@ -128,7 +157,7 @@ foreach(protocol_file ${protocol_files})
endforeach()
set(documentation_files
- lib/bbackupquery/documentation.txt
+ lib/bbackupquery/Documentation.txt
)
foreach(documentation_file ${documentation_files})
@@ -148,12 +177,7 @@ foreach(documentation_file ${documentation_files})
endforeach()
file(STRINGS ${base_dir}/modules.txt module_deps REGEX "^[^#]")
-# qdbm, lib/common and lib/win32 aren't listed in modules.txt, so hard-code them.
-foreach(module_dep
- "qdbm"
- "lib/win32"
- "lib/common qdbm lib/win32"
- ${module_deps})
+foreach(module_dep ${module_deps})
string(REGEX MATCH "([^ ]+)[ ]*(.*)" valid_module_line ${module_dep})
if(valid_module_line)
@@ -165,16 +189,19 @@ foreach(module_dep
set(module_path ${base_dir}/${module_dir})
string(REPLACE "/" "_" module_name ${CMAKE_MATCH_1})
string(REPLACE "/" "_" dependencies "${CMAKE_MATCH_2}")
- file(GLOB module_files ${module_path}/*.c ${module_path}/*.cpp
- ${module_path}/*.h)
- set(module_files ${module_files} ${${module_name}_extra_files})
- # everything except qdbm, lib/common and lib/win32 implicitly depend on
- # lib/common, so express that dependency here.
- if(module_name MATCHES "^(qdbm|lib_(common|win32))$")
+ # We are replacing QDBM's normal build system, and we only want to include
+ # the modules that we actually need, to avoid warnings about duplicate
+ # definitions, and potential conflicts with Box Backup code in future, so
+ # we specify the C files to compile in explicitly.
+ if(module_name STREQUAL "qdbm")
+ file(GLOB module_files ${module_path}/depot.c ${module_path}/myconf.c)
else()
- set(dependencies "${dependencies} lib_common")
+ file(GLOB module_files ${module_path}/*.cpp ${module_path}/*.h)
endif()
+
+ set(module_files ${module_files} ${${module_name}_extra_files})
+
string(REGEX REPLACE "^ " "" dependencies "${dependencies}")
string(REGEX REPLACE " $" "" dependencies "${dependencies}")
@@ -185,6 +212,18 @@ foreach(module_dep
message(STATUS "add executable '${module_name}': '${module_files}'")
endif()
add_executable(${module_name} ${module_files})
+
+ # Unfortunately we have to use install(PROGRAMS) instead of
+ # install(TARGETS) because TARGETS doesn't allow us to change
+ # the executable name.
+ install(PROGRAMS "$<TARGET_FILE:${module_name}>"
+ CONFIGURATIONS Debug
+ DESTINATION "${base_dir}/debug/${module_dir}"
+ RENAME "${bin_name}${CMAKE_EXECUTABLE_SUFFIX}")
+ install(PROGRAMS "$<TARGET_FILE:${module_name}>"
+ CONFIGURATIONS Release
+ DESTINATION "${base_dir}/release/${module_dir}"
+ RENAME "${bin_name}${CMAKE_EXECUTABLE_SUFFIX}")
elseif(module_name MATCHES "^test_")
string(REGEX MATCH "^test_(.*)" valid_test ${module_name})
set(test_name ${CMAKE_MATCH_1})
@@ -195,12 +234,62 @@ foreach(module_dep
endif()
string(REPLACE "TEST_NAME" ${test_name} test_main "${test_template}")
- file(WRITE "${module_path}/_main.cpp" "${test_main}")
+ file(WRITE "${module_path}/_main.cpp.new" "${test_main}")
+ replace_file_if_different(
+ "${module_path}/_main.cpp"
+ "${module_path}/_main.cpp.new")
add_executable(${module_name} ${module_files}
"${module_path}/_main.cpp")
+
+ if(WIN32)
+ install(PROGRAMS "$<TARGET_FILE:${module_name}>"
+ CONFIGURATIONS Debug
+ DESTINATION "${base_dir}/debug/${module_dir}")
+ install(PROGRAMS "$<TARGET_FILE:${module_name}>"
+ CONFIGURATIONS Release
+ DESTINATION "${base_dir}/release/${module_dir}")
+ set(test_executable "$<TARGET_FILE_NAME:${module_name}>")
+ else()
+ # Unfortunately we have to use install(PROGRAMS) instead of
+ # install(TARGETS) because TARGETS doesn't allow us to change
+ # the executable name.
+ install(PROGRAMS "$<TARGET_FILE:${module_name}>"
+ CONFIGURATIONS Debug
+ DESTINATION "${base_dir}/debug/${module_dir}"
+ RENAME "_test")
+ install(PROGRAMS "$<TARGET_FILE:${module_name}>"
+ CONFIGURATIONS Release
+ DESTINATION "${base_dir}/release/${module_dir}"
+ RENAME "_test")
+ set(test_executable "./_test")
+ endif()
+
+ if(${APPVEYOR_MODE})
+ set(appveyor_runtest_pl_switch -a)
+ else()
+ set(appveyor_runtest_pl_switch)
+ endif()
+
+ target_compile_definitions(${module_name} PRIVATE
+ -DTEST_EXECUTABLE="${test_executable}")
add_test(NAME ${test_name}
- COMMAND ${PERL_EXECUTABLE} ${base_dir}/runtest.pl.in ${test_name}
- $<CONFIG> WORKING_DIRECTORY ${base_dir})
+ COMMAND ${PERL_EXECUTABLE} ${base_dir}/runtest.pl
+ ${appveyor_runtest_pl_switch} -c ${test_name}
+ $<CONFIG> "$<TARGET_FILE:${module_name}>" "${test_executable}"
+ WORKING_DIRECTORY ${base_dir})
+
+ if(${APPVEYOR_MODE})
+ execute_process(COMMAND appveyor AddTest -Name ${test_name}
+ -Framework Custom -FileName "")
+ endif()
+
+ # It helps with debugging if the test depends on another step which
+ # prepares the target directory, and is always out of date.
+ add_custom_target(${module_name}-prepare
+ COMMAND ${PERL_EXECUTABLE} ${base_dir}/runtest.pl
+ -n -c ${test_name}
+ $<CONFIG> "$<TARGET_FILE:${module_name}>" "${test_executable}"
+ WORKING_DIRECTORY ${base_dir})
elseif(module_name MATCHES "^(lib_.*|qdbm)$")
if(DEBUG)
message(STATUS "add library '${module_name}': '${module_files}'")
@@ -210,19 +299,6 @@ foreach(module_dep
message(FATAL_ERROR "Unsupported module type: " ${module_name})
endif()
- if(module_name MATCHES "^(bin|test)_")
- # We need to install binaries in specific places so that test
- # runner can find them:
- install(FILES "$<TARGET_FILE:${module_name}>"
- CONFIGURATIONS Debug
- DESTINATION "${base_dir}/debug/${module_dir}"
- RENAME "${bin_name}${CMAKE_EXECUTABLE_SUFFIX}")
- install(FILES "$<TARGET_FILE:${module_name}>"
- CONFIGURATIONS Release
- DESTINATION "${base_dir}/release/${module_dir}"
- RENAME "${bin_name}${CMAKE_EXECUTABLE_SUFFIX}")
- endif()
-
target_compile_definitions(${module_name} PRIVATE -DBOX_MODULE="${module_name}")
if(dependencies)
@@ -254,47 +330,359 @@ foreach(module_dep
endif()
endforeach()
-#include(ExternalProject)
-#ExternalProject_Add(pcre
-# PREFIX "../pcre"
-# BUILD_COMMAND "${CMAKE_EXECUTABLE
+# We can't do anything conditional on CMAKE_BUILD_TYPE because that's not valid for multi-configuration
+# generators such as MSVC. We need to use a generator expression instead.
+target_compile_definitions(lib_common PUBLIC $<$<CONFIG:Release>:BOX_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
include_directories(${base_dir}/../zlib-win32/include)
-find_library(zlibstaticd_lib_path zlibstaticd ${base_dir}/../zlib-win32/lib)
-find_library(zlibstatic_lib_path zlibstatic ${base_dir}/../zlib-win32/lib)
-target_link_libraries(lib_compress PUBLIC debug ${zlibstaticd_lib_path})
-target_link_libraries(lib_compress PUBLIC optimized ${zlibstatic_lib_path})
+# On Windows we want to statically link zlib to make debugging and distribution easier,
+# but FindZLIB.cmake doesn't offer that as an option, so we have to go through some
+# contortions to "find" the correct library. ZLIB_ROOT is required in this case.
+if(WIN32)
+ if(NOT DEFINED ZLIB_ROOT)
+ message(FATAL_ERROR "You must set ZLIB_ROOT to point to include/zlib.h and lib/zlibstatic[d].lib on Windows")
+ endif()
+
+ message(STATUS "Searching for Zlib in: ${ZLIB_ROOT}")
+ find_path(ZLIB_INCLUDE_DIR zlib.h PATHS ${ZLIB_ROOT}/include NO_DEFAULT_PATH)
+ include_directories(${ZLIB_INCLUDE_DIR})
+ message(STATUS "Found Zlib headers: ${ZLIB_INCLUDE_DIR}")
+
+ # We must link against zlibstaticD if this is a debug build, otherwise
+ # we have a C runtime mismatch (/MD versus /MDd) and the application
+ # crashes at runtime.
+ find_library(ZLIB_LIBRARY_STATIC_DEBUG NAMES zlibstaticd
+ PATHS ${ZLIB_ROOT}/lib NO_DEFAULT_PATH)
+ find_library(ZLIB_LIBRARY_STATIC_RELEASE NAMES zlibstatic
+ PATHS ${ZLIB_ROOT}/lib NO_DEFAULT_PATH)
+
+ target_link_libraries(lib_compress PUBLIC
+ debug ${ZLIB_LIBRARY_STATIC_DEBUG}
+ optimized ${ZLIB_LIBRARY_STATIC_RELEASE})
+else()
+ find_package(ZLIB REQUIRED)
+ include_directories(${ZLIB_INCLUDE_DIRS})
+ target_link_libraries(lib_compress PUBLIC ${ZLIB_LIBRARIES})
+endif()
# Link to OpenSSL
-include_directories(${base_dir}/../openssl-win32/include)
-find_library(libeay32_lib_path libeay32 ${base_dir}/../openssl-win32/lib)
-find_library(ssleay32_lib_path ssleay32 ${base_dir}/../openssl-win32/lib)
-target_link_libraries(lib_crypto PUBLIC ${libeay32_lib_path} ${ssleay32_lib_path})
+find_package(OpenSSL REQUIRED)
+include_directories(${OPENSSL_INCLUDE_DIR})
+target_link_libraries(lib_crypto PUBLIC ${OPENSSL_LIBRARIES})
# Link to PCRE
-include_directories(${base_dir}/../pcre-win32/include)
-target_compile_definitions(lib_common PUBLIC -DPCRE_STATIC)
-find_library(pcreposix_lib_path pcreposix ${base_dir}/../pcre-win32/lib)
-find_library(pcreposixd_lib_path pcreposixd ${base_dir}/../pcre-win32/lib)
-find_library(pcre_lib_path pcre ${base_dir}/../pcre-win32/lib)
-find_library(pcred_lib_path pcred ${base_dir}/../pcre-win32/lib)
-target_link_libraries(lib_common PUBLIC debug "${pcreposixd_lib_path}" optimized "${pcreposix_lib_path}")
-target_link_libraries(lib_common PUBLIC debug "${pcred_lib_path}" optimized "${pcre_lib_path}")
+if (WIN32)
+ if(NOT DEFINED PCRE_ROOT)
+ message(FATAL_ERROR "You must set PCRE_ROOT to point to include/pcreposix.h and lib/pcreposix[d].lib on Windows")
+ endif()
+
+ target_compile_definitions(lib_common PUBLIC -DPCRE_STATIC)
+ find_library(pcreposix_lib_path pcreposix ${PCRE_ROOT}/lib)
+ find_library(pcreposixd_lib_path pcreposixd ${PCRE_ROOT}/lib)
+ find_library(pcre_lib_path pcre ${PCRE_ROOT}/lib)
+ find_library(pcred_lib_path pcred ${PCRE_ROOT}/lib)
+ target_link_libraries(lib_common PUBLIC debug "${pcreposixd_lib_path}" optimized "${pcreposix_lib_path}")
+ target_link_libraries(lib_common PUBLIC debug "${pcred_lib_path}" optimized "${pcre_lib_path}")
+ include_directories(${PCRE_ROOT}/include)
+else()
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(PCRE REQUIRED libpcreposix)
+ include_directories(${PCRE_INCLUDE_DIRS})
+ target_link_libraries(lib_common PUBLIC ${PCRE_LIBRARIES})
+
+ if(DEBUG)
+ message(STATUS "Linking PCRE libraries from ${PCRE_LIBRARY_DIRS}: ${PCRE_LIBRARIES}")
+ endif()
+endif()
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}")
+find_package(Readline)
+if(READLINE_FOUND)
+ include_directories(${Readline_INCLUDE_DIR})
+ 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
-cmake_to_native_path("${PERL_EXECUTABLE}" perl_executable_native)
+file(TO_NATIVE_PATH "${PERL_EXECUTABLE}" perl_executable_native)
string(REPLACE "\\" "\\\\" perl_path_escaped ${perl_executable_native})
target_compile_definitions(test_backupstorefix PRIVATE -DPERL_EXECUTABLE="${perl_path_escaped}")
+
+# Configure test timeouts:
+# I've set the timeout to 4 times as long as it took to run on a particular run on Appveyor:
+# https://ci.appveyor.com/project/qris/boxbackup/build/job/xm10itascygtu93j
+set_tests_properties(common PROPERTIES TIMEOUT 20)
+set_tests_properties(crypto PROPERTIES TIMEOUT 4)
+set_tests_properties(compress PROPERTIES TIMEOUT 80)
+set_tests_properties(raidfile PROPERTIES TIMEOUT 32)
+set_tests_properties(basicserver PROPERTIES TIMEOUT 80)
+set_tests_properties(backupstore PROPERTIES TIMEOUT 560)
+set_tests_properties(backupstorefix PROPERTIES TIMEOUT 140)
+set_tests_properties(backupstorepatch PROPERTIES TIMEOUT 320)
+set_tests_properties(backupdiff PROPERTIES TIMEOUT 32)
+set_tests_properties(bbackupd PROPERTIES TIMEOUT 1200)
+set_tests_properties(s3store PROPERTIES TIMEOUT 20)
+set_tests_properties(httpserver PROPERTIES TIMEOUT 40)
diff --git a/infrastructure/cmake/FindReadline.cmake b/infrastructure/cmake/FindReadline.cmake
new file mode 100644
index 00000000..3ba4d21d
--- /dev/null
+++ b/infrastructure/cmake/FindReadline.cmake
@@ -0,0 +1,84 @@
+# https://github.com/bro/cmake/blob/master/FindReadline.cmake
+#
+# Copyright (c) 1995-2015, The Regents of the University of California
+# through the Lawrence Berkeley National Laboratory and the
+# International Computer Science Institute. 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 University of California, Lawrence Berkeley
+# National Laboratory, U.S. Dept. of Energy, International Computer
+# Science Institute, nor the names of 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 OWNER 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.
+#
+# Note that some files in the distribution may carry their own copyright
+# notices.
+
+# - Try to find readline include dirs and libraries
+#
+# Usage of this module as follows:
+#
+# find_package(Readline)
+#
+# Variables used by this module, they can change the default behaviour and need
+# to be set before calling find_package:
+#
+# Readline_ROOT_DIR Set this variable to the root installation of
+# readline if the module has problems finding the
+# proper installation path.
+#
+# Variables defined by this module:
+#
+# READLINE_FOUND System has readline, include and lib dirs found
+# Readline_INCLUDE_DIR The readline include directories.
+# Readline_LIBRARY The readline library.
+
+find_path(Readline_ROOT_DIR
+ NAMES include/readline/readline.h
+)
+
+find_path(Readline_INCLUDE_DIR
+ NAMES readline/readline.h
+ HINTS ${Readline_ROOT_DIR}/include
+)
+
+find_library(Readline_LIBRARY
+ NAMES readline
+ HINTS ${Readline_ROOT_DIR}/lib
+)
+
+if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+ set(READLINE_FOUND TRUE)
+else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+ FIND_LIBRARY(Readline_LIBRARY NAMES readline)
+ include(FindPackageHandleStandardArgs)
+ FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY )
+ MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY)
+endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+
+mark_as_advanced(
+ Readline_ROOT_DIR
+ Readline_INCLUDE_DIR
+ Readline_LIBRARY
+)
diff --git a/infrastructure/cmake/getversion.pl b/infrastructure/cmake/getversion.pl
new file mode 100755
index 00000000..67e1f8a5
--- /dev/null
+++ b/infrastructure/cmake/getversion.pl
@@ -0,0 +1,13 @@
+#!perl
+
+use File::Basename;
+$basedir = dirname($0)."/../..";
+
+-d $basedir or die "Base directory $basedir does not exist!";
+chdir $basedir or die "Failed to change to base directory: $basedir: $!";
+
+require "$basedir/infrastructure/BoxPlatform.pm.in";
+
+print "#define BOX_VERSION \"$BoxPlatform::product_version\"\n";
+
+exit 0;
diff --git a/infrastructure/cmake/msvc/bin_bbackupd.vcxproj.user b/infrastructure/cmake/msvc/bin_bbackupd.vcxproj.user
new file mode 100755
index 00000000..fa1f3d34
--- /dev/null
+++ b/infrastructure/cmake/msvc/bin_bbackupd.vcxproj.user
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LocalDebuggerCommandArguments>testfiles\bbackupd.conf</LocalDebuggerCommandArguments>
+ <LocalDebuggerWorkingDirectory>$(ProjectDir)\..\..\..\debug\test\bbackupd</LocalDebuggerWorkingDirectory>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ </PropertyGroup>
+</Project> \ No newline at end of file
diff --git a/infrastructure/cmake/msvc/bin_bbstored.vcxproj.user b/infrastructure/cmake/msvc/bin_bbstored.vcxproj.user
new file mode 100755
index 00000000..339cddee
--- /dev/null
+++ b/infrastructure/cmake/msvc/bin_bbstored.vcxproj.user
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LocalDebuggerCommandArguments>testfiles/bbstored.conf</LocalDebuggerCommandArguments>
+ <LocalDebuggerWorkingDirectory>$(ProjectDir)\..\..\..\debug\test\backupstorefix</LocalDebuggerWorkingDirectory>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ </PropertyGroup>
+</Project> \ No newline at end of file
diff --git a/infrastructure/cmake/msvc/test_backupstore.vcxproj.user b/infrastructure/cmake/msvc/test_backupstore.vcxproj.user
new file mode 100755
index 00000000..7d7b3158
--- /dev/null
+++ b/infrastructure/cmake/msvc/test_backupstore.vcxproj.user
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LocalDebuggerWorkingDirectory>$(ProjectDir)\..\..\..\debug\test\backupstore</LocalDebuggerWorkingDirectory>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ <LocalDebuggerCommandArguments>
+ </LocalDebuggerCommandArguments>
+ </PropertyGroup>
+</Project> \ No newline at end of file
diff --git a/infrastructure/cmake/msvc/test_backupstorefix.vcxproj.user b/infrastructure/cmake/msvc/test_backupstorefix.vcxproj.user
new file mode 100755
index 00000000..170fb496
--- /dev/null
+++ b/infrastructure/cmake/msvc/test_backupstorefix.vcxproj.user
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LocalDebuggerWorkingDirectory>$(ProjectDir)\..\..\..\debug\test\backupstorefix</LocalDebuggerWorkingDirectory>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ </PropertyGroup>
+</Project> \ No newline at end of file
diff --git a/infrastructure/cmake/msvc/test_bbackupd.vcxproj.user b/infrastructure/cmake/msvc/test_bbackupd.vcxproj.user
new file mode 100755
index 00000000..ebf8c6a3
--- /dev/null
+++ b/infrastructure/cmake/msvc/test_bbackupd.vcxproj.user
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LocalDebuggerCommandArguments>-e test_basics</LocalDebuggerCommandArguments>
+ <LocalDebuggerWorkingDirectory>$(ProjectDir)\..\..\..\debug\test\bbackupd</LocalDebuggerWorkingDirectory>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ </PropertyGroup>
+</Project> \ No newline at end of file
diff --git a/infrastructure/cmake/msvc/test_common.vcxproj.user b/infrastructure/cmake/msvc/test_common.vcxproj.user
new file mode 100755
index 00000000..e5854a80
--- /dev/null
+++ b/infrastructure/cmake/msvc/test_common.vcxproj.user
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LocalDebuggerWorkingDirectory>$(ProjectDir)\..\..\..\debug\test\common</LocalDebuggerWorkingDirectory>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ </PropertyGroup>
+</Project> \ No newline at end of file
diff --git a/infrastructure/cmake/msvc/test_httpserver.vcxproj.user b/infrastructure/cmake/msvc/test_httpserver.vcxproj.user
new file mode 100755
index 00000000..ac1512a8
--- /dev/null
+++ b/infrastructure/cmake/msvc/test_httpserver.vcxproj.user
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LocalDebuggerWorkingDirectory>$(ProjectDir)\..\..\..\debug\test\httpserver</LocalDebuggerWorkingDirectory>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ </PropertyGroup>
+</Project> \ No newline at end of file
diff --git a/infrastructure/cmake/msvc/test_raidfile.vcxproj.user b/infrastructure/cmake/msvc/test_raidfile.vcxproj.user
new file mode 100755
index 00000000..620aa4bb
--- /dev/null
+++ b/infrastructure/cmake/msvc/test_raidfile.vcxproj.user
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LocalDebuggerWorkingDirectory>$(ProjectDir)\..\..\..\debug\test\raidfile</LocalDebuggerWorkingDirectory>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ </PropertyGroup>
+</Project> \ No newline at end of file
diff --git a/infrastructure/cmake/windows/CMakeLists.txt b/infrastructure/cmake/windows/CMakeLists.txt
new file mode 100644
index 00000000..42152714
--- /dev/null
+++ b/infrastructure/cmake/windows/CMakeLists.txt
@@ -0,0 +1,89 @@
+cmake_minimum_required(VERSION 2.6)
+
+project(BoxBackup_Windows)
+
+set(boxbackup_dir ${CMAKE_SOURCE_DIR}/../../..)
+set_property(DIRECTORY PROPERTY EP_PREFIX .)
+set(install_dir ${CMAKE_BINARY_DIR}/install)
+
+# Automate the process of downloading, building and "installing" dependencies on Windows,
+# as used by AppVeyor.
+set(ZLIB_VERSION 1.2.8 CACHE STRING "Version of zlib to download, build, and compile Box Backup against")
+set(ZLIB_HASH MD5=126f8676442ffbd97884eb4d6f32afb4
+ CACHE STRING "Hash of the zlib download file, to be verified after download")
+set(OPENSSL_VERSION 1.0.2h CACHE STRING "Version of OpenSSL to download, build, and compile Box Backup against")
+set(OPENSSL_HASH SHA256=1d4007e53aad94a5b2002fe045ee7bb0b3d98f1a47f8b2bc851dcd1c74332919
+ CACHE STRING "Hash of the OpenSSL download file, to be verified after download")
+set(PCRE_VERSION 8.38 CACHE STRING "Version of PCRE to download, build, and compile Box Backup against")
+set(OPENSSL_HASH SHA256=dbef7cf80258c29396d435804cd5dba34006a77548850bca8bad6db6a6eac110
+ CACHE STRING "Hash of the PCRE download file, to be verified after download")
+
+include(ExternalProject)
+
+string(REPLACE "." "" zlib_version_nodots ${ZLIB_VERSION})
+ExternalProject_Add(zlib
+ URL "http://zlib.net/zlib${zlib_version_nodots}.zip"
+ URL_HASH ${ZLIB_HASH}
+ DOWNLOAD_NO_PROGRESS 1
+ CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${install_dir}
+ # We need to build both versions, debug and release, because cmake requires both to be
+ # present to generate its multi-configuration project files for Visual Studio/MSBuild.
+ INSTALL_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target install --config Debug
+ COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target install --config Release
+ STEP_TARGETS configure install
+)
+
+if(WIN32)
+ ExternalProject_Add(openssl
+ DEPENDS zlib
+ URL "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz"
+ URL_HASH ${OPENSSL_HASH}
+ DOWNLOAD_NO_PROGRESS 1
+ CONFIGURE_COMMAND perl Configure debug-VC-WIN32 no-asm --prefix=${install_dir}
+ COMMAND cmd /c ms\\do_ms.bat
+ # You would expect us to use nt.mak to compile a static library here, but mk1mf.pl uses the /MT[d]
+ # CRT in that case, which is incompatible with our dynamic runtime, /MD[d]. It seems that the libs
+ # built by ntdll.mak, which are compiled with /MD[d], are full libraries and not import libs,
+ # so we can link statically against them and still get a dynamic runtime.
+ BUILD_IN_SOURCE 1
+ BUILD_COMMAND nmake /s /f ms\\nt.mak
+ INSTALL_COMMAND nmake /s /f ms\\nt.mak install
+ )
+elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ ExternalProject_Add(openssl
+ DEPENDS zlib
+ URL "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz"
+ URL_HASH ${OPENSSL_HASH}
+ DOWNLOAD_NO_PROGRESS 1
+ CONFIGURE_COMMAND perl Configure darwin64-x86_64-cc --prefix=${install_dir}
+ BUILD_IN_SOURCE 1
+ )
+else()
+ ExternalProject_Add(openssl
+ DEPENDS zlib
+ URL "https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz"
+ URL_HASH ${OPENSSL_HASH}
+ DOWNLOAD_NO_PROGRESS 1
+ CONFIGURE_COMMAND ./config --prefix=${install_dir}
+ BUILD_IN_SOURCE 1
+ )
+
+endif()
+
+ExternalProject_Add(pcre
+ URL "http://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-${PCRE_VERSION}.zip"
+ URL_HASH ${PCRE_HASH}
+ DOWNLOAD_NO_PROGRESS 1
+ CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${install_dir}
+ # We need to build both versions, debug and release, because cmake requires both to be
+ # present to generate its multi-configuration project files for Visual Studio/MSBuild.
+ INSTALL_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target install --config Debug
+ COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target install --config Release
+)
+
+ExternalProject_Add(boxbackup
+ DEPENDS zlib openssl pcre
+ SOURCE_DIR ${boxbackup_dir}/infrastructure/cmake
+ CMAKE_ARGS -DZLIB_ROOT=${install_dir} -DOPENSSL_ROOT_DIR=${install_dir} -DPCRE_ROOT=${install_dir} -DAPPVEYOR_MODE=1
+ STEP_TARGETS configure build install
+)