# Copyright (C) 2015 Adam Schubert # Copyright 2016 Martin Pool # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # # 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. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. project(librsync C) cmake_minimum_required(VERSION 3.6) INCLUDE(CMakeDependentOption) include(GNUInstallDirs) set(LIBRSYNC_MAJOR_VERSION 2) set(LIBRSYNC_MINOR_VERSION 3) set(LIBRSYNC_PATCH_VERSION 5) set(LIBRSYNC_VERSION ${LIBRSYNC_MAJOR_VERSION}.${LIBRSYNC_MINOR_VERSION}.${LIBRSYNC_PATCH_VERSION}) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # Turn on generating compile_commands.json for clang-tidy and iwyu. set(CMAKE_EXPORT_COMPILE_COMMANDS ON) if (NOT CMAKE_SYSTEM_PROCESSOR) message(FATAL_ERROR "No target CPU architecture set") endif() if (NOT CMAKE_SYSTEM_NAME) message(FATAL_ERROR "No target OS set") endif() # Set CMAKE_BUILD_TYPE if unset. include(BuildType) message (STATUS "CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}") option(BUILD_SHARED_LIBS "Build librsync as a shared library." ON) # Option ENABLE_TRACE defaults to ON for Debug builds. if (CMAKE_BUILD_TYPE MATCHES Debug) option(ENABLE_TRACE "Compile in detailed trace messages" ON) else () option(ENABLE_TRACE "Compile in detailed trace messages" OFF) endif() set(DO_RS_TRACE 0) if (ENABLE_TRACE) set(DO_RS_TRACE 1) endif (ENABLE_TRACE) message(STATUS "DO_RS_TRACE=${DO_RS_TRACE}") # Add an option to include compression support option(ENABLE_COMPRESSION "Whether or not to build with compression support" OFF) # TODO: Remove this warning when compression is implemented. # Consider turning compression ON by default. if (ENABLE_COMPRESSION) message(WARNING "Compression support is not functional. See issue #8.") endif (ENABLE_COMPRESSION) include ( CheckIncludeFiles ) check_include_files ( sys/file.h HAVE_SYS_FILE_H ) check_include_files ( sys/stat.h HAVE_SYS_STAT_H ) check_include_files ( sys/types.h HAVE_SYS_TYPES_H ) check_include_files ( unistd.h HAVE_UNISTD_H ) check_include_files ( io.h HAVE_IO_H ) check_include_files ( fcntl.h HAVE_FCNTL_H ) check_include_files ( mcheck.h HAVE_MCHECK_H ) check_include_files ( zlib.h HAVE_ZLIB_H ) check_include_files ( bzlib.h HAVE_BZLIB_H ) # Remove compression support if not needed if (NOT ENABLE_COMPRESSION) SET(HAVE_BZLIB_H 0) SET(HAVE_ZLIB_H 0) endif (NOT ENABLE_COMPRESSION) include ( CheckSymbolExists ) check_symbol_exists ( __func__ "" HAVE___FUNC__ ) check_symbol_exists ( __FUNCTION__ "" HAVE___FUNCTION__ ) include ( CheckFunctionExists ) check_function_exists ( fseeko HAVE_FSEEKO ) check_function_exists ( fseeko64 HAVE_FSEEKO64 ) check_function_exists ( _fseeki64 HAVE__FSEEKI64 ) check_function_exists ( fstat64 HAVE_FSTAT64 ) check_function_exists ( _fstati64 HAVE__FSTATI64 ) check_function_exists ( fileno HAVE_FILENO ) check_function_exists ( _fileno HAVE__FILENO ) include(CheckTypeSize) check_type_size ( "long" SIZEOF_LONG ) check_type_size ( "long long" SIZEOF_LONG_LONG ) check_type_size ( "off_t" SIZEOF_OFF_T ) check_type_size ( "off64_t" SIZEOF_OFF64_T ) check_type_size ( "size_t" SIZEOF_SIZE_T ) check_type_size ( "unsigned int" SIZEOF_UNSIGNED_INT ) check_type_size ( "unsigned long" SIZEOF_UNSIGNED_LONG ) check_type_size ( "unsigned short" SIZEOF_UNSIGNED_SHORT ) # Check for printf "%zu" size_t formatting support. if(WIN32) # CheckCSourceRuns checking for "%zu" succeeds but still gives warnings on win32. set(HAVE_PRINTF_Z OFF) # Not using unsupported %zu generates warnings about using %I64 with MinGW. # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format") message (STATUS "Compiling to Win32 - printf \"%zu\" size_t formatting support disabled") elseif(CMAKE_CROSSCOMPILING) # CheckCSourceRuns doesn't work when cross-compiling; assume C99 compliant support. set(HAVE_PRINTF_Z ON) message (STATUS "Cross compiling - assuming printf \"%zu\" size_t formatting support") else() include(CheckCSourceRuns) check_c_source_runs("#include \nint main(){char o[8];sprintf(o, \"%zu\", (size_t)7);return o[0] != '7';}" HAVE_PRINTF_Z) endif() include (TestBigEndian) TEST_BIG_ENDIAN(WORDS_BIGENDIAN) if (WORDS_BIGENDIAN) message(STATUS "System is big-endian.") else (WORDS_BIGENDIAN) message(STATUS "System is little-endian.") endif (WORDS_BIGENDIAN) # OS X if(APPLE) set(CMAKE_MACOSX_RPATH ON) set(CMAKE_SKIP_BUILD_RPATH FALSE) set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" isSystemDir) if("${isSystemDir}" STREQUAL "-1") set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") endif() endif() if (CMAKE_C_COMPILER_ID MATCHES "(Clang|Gnu|GNU)") # TODO: Set -Werror when the build is clean. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -std=c99 -pedantic") if (CMAKE_C_COMPILER_ID MATCHES "Clang") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-sign-conversion") endif() elseif(CMAKE_C_COMPILER_ID MATCHES "MSVC") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D_CRT_SECURE_NO_WARNINGS") endif() site_name(BUILD_HOSTNAME) message (STATUS "PROJECT_NAME = ${PROJECT_NAME}") message (STATUS "BUILD_HOSTNAME = ${BUILD_HOSTNAME}") message (STATUS "CMAKE_SYSTEM = ${CMAKE_SYSTEM}") # Find POPT find_package(POPT) if (POPT_FOUND) message (STATUS "POPT_INCLUDE_DIRS = ${POPT_INCLUDE_DIRS}") message (STATUS "POPT_LIBRARIES = ${POPT_LIBRARIES}") include_directories(${POPT_INCLUDE_DIRS}) endif (POPT_FOUND) # Add an option to exclude rdiff executable from build # This is useful, because it allows to remove POPT dependency if a user is interested only in the # rsync library itself and not in the rdiff executable cmake_dependent_option(BUILD_RDIFF "Whether or not to build rdiff executable" ON "POPT_FOUND" OFF) # Find BZIP find_package (BZip2) if (BZIP2_FOUND) message (STATUS "BZIP2_INCLUDE_DIRS = ${BZIP2_INCLUDE_DIRS}") message (STATUS "BZIP2_LIBRARIES = ${BZIP2_LIBRARIES}") include_directories(${BZIP2_INCLUDE_DIRS}) endif (BZIP2_FOUND) # Find ZLIB find_package (ZLIB) if (ZLIB_FOUND) message (STATUS "ZLIB_INCLUDE_DIRS = ${ZLIB_INCLUDE_DIRS}") message (STATUS "ZLIB_LIBRARIES = ${ZLIB_LIBRARIES}") include_directories(${ZLIB_INCLUDE_DIRS}) endif (ZLIB_FOUND) # Find libb2 find_package(LIBB2) if (LIBB2_FOUND) message (STATUS "LIBB2_INCLUDE_DIRS = ${LIBB2_INCLUDE_DIRS}") message (STATUS "LIBB2_LIBRARIES = ${LIBB2_LIBRARIES}") endif (LIBB2_FOUND) # Add an option to use LIBB2 if found. It defaults to off because the # reference implementation is currently faster. cmake_dependent_option(USE_LIBB2 "Use the libb2 blake2 implementation." OFF "LIBB2_FOUND" OFF) if (USE_LIBB2) message (STATUS "Using libb2 blake2 implementation.") include_directories(${LIBB2_INCLUDE_DIRS}) set(blake2_LIBS ${LIBB2_LIBRARIES}) else (USE_LIBB2) message (STATUS "Using included blake2 implementation.") include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/blake2) set(blake2_SRCS src/blake2/blake2b-ref.c) endif (USE_LIBB2) # Doxygen doc generator. find_package(Doxygen) if(DOXYGEN_FOUND) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile @ONLY) add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM ) endif(DOXYGEN_FOUND) # Code tidy target to reformat code with indent. file(GLOB tidy_SRCS src/*.[ch] tests/*.[ch]) set(TYPE_RE "(\\w+_t)") set(CAST_RE "(\\(${TYPE_RE}( \\*+)?\\))") add_custom_target(tidy COMMENT "Reformatting all code to preferred coding style." # Note indent requires all userdefined types to be specified with '-T ' args to # format them correctly. Rather than do that, we just postprocess with sed. # # Hide the enclosing 'extern "C" {...}' block for indenting in librsync.h COMMAND sed -r -i "s:^(extern \"C\") \\{:\\1;:; s:^\\}(\\s+/\\* extern \"C\" \\*/):;\\1:" src/librsync.h # Linux format with no tabs, indent 4, preproc indent 2, 80 columns, swallow blank lines. COMMAND indent -linux -nut -i4 -ppi2 -l80 -lc80 -fc1 -sob -T FILE -T Rollsum -T rs_result ${tidy_SRCS} # Remove space between * or & and identifier after userdefined types, # remove space after type cast for userdefined types like indent -ncs, # and remove trailing whitespace. COMMAND sed -r -i "s:((${TYPE_RE}|${CAST_RE}) (&|\\*+)) :\\1:g; s:(${CAST_RE}) :\\1:g; s:\\s+$::" ${tidy_SRCS} # Restore the enclosing 'extern "C" {...}' block in librsync.h COMMAND sed -r -i "s:^(extern \"C\");:\\1 {:; s:^;(\\s+/\\* extern \"C\" \\*/):}\\1:" src/librsync.h VERBATIM ) # Code tidyc target to reformat all code and comments with https://github.com/dbaarda/tidyc. add_custom_target(tidyc COMMENT "Reformatting all code and comments to preferred coding style." # Recomended format, reformat linebreaks, reformat comments, 80 columns. COMMAND tidyc -R -C -l80 -T FILE -T Rollsum -T rs_result ${tidy_SRCS} VERBATIM ) # clang-tidy target to check code for errors. add_custom_target(clang-tidy COMMENT "Check code for errors using clang-tidy." COMMAND run-clang-tidy -p ${CMAKE_CURRENT_BINARY_DIR} VERBATIM ) # iwyu target to check includes for correctness. # Note we ignore noisy "note:" output. add_custom_target(iwyu COMMENT "Check #includes for correctness using iwyu_tool." COMMAND ! iwyu_tool -p ${CMAKE_CURRENT_BINARY_DIR} -o clang | egrep -v "note:" VERBATIM ) # iwyu-fix target to fix includes for correctness. add_custom_target(iwyu-fix COMMENT "Fix #includes for correctness using iwyu_tool and fix_include." COMMAND iwyu_tool -p ${CMAKE_CURRENT_BINARY_DIR} | fix_include --noblank_lines VERBATIM ) # Testing add_executable(isprefix_test tests/isprefix_test.c src/isprefix.c) add_test(NAME isprefix_test COMMAND isprefix_test) add_executable(netint_test tests/netint_test.c src/netint.c src/util.c src/trace.c src/tube.c src/scoop.c) target_compile_options(netint_test PRIVATE -DLIBRSYNC_STATIC_DEFINE) add_test(NAME netint_test COMMAND netint_test) add_executable(rollsum_test tests/rollsum_test.c src/rollsum.c) add_test(NAME rollsum_test COMMAND rollsum_test) add_executable(rabinkarp_test tests/rabinkarp_test.c src/rabinkarp.c) add_test(NAME rabinkarp_test COMMAND rabinkarp_test) add_executable(rabinkarp_perf tests/rabinkarp_perf.c src/rabinkarp.c) add_executable(hashtable_test tests/hashtable_test.c src/hashtable.c) add_test(NAME hashtable_test COMMAND hashtable_test) add_executable(checksum_test tests/checksum_test.c src/checksum.c src/rollsum.c src/rabinkarp.c src/mdfour.c ${blake2_SRCS}) target_compile_options(checksum_test PRIVATE -DLIBRSYNC_STATIC_DEFINE) target_link_libraries(checksum_test ${blake2_LIBS}) add_test(NAME checksum_test COMMAND checksum_test) add_executable(sumset_test tests/sumset_test.c src/sumset.c src/util.c src/trace.c src/hex.c src/checksum.c src/rollsum.c src/rabinkarp.c src/mdfour.c src/hashtable.c ${blake2_SRCS}) target_compile_options(sumset_test PRIVATE -DLIBRSYNC_STATIC_DEFINE) target_link_libraries(sumset_test ${blake2_LIBS}) add_test(NAME sumset_test COMMAND sumset_test) # On Windows we need to explicitly execute bash for scripts. if (WIN32) set(WIN_BASH bash -e) endif (WIN32) # Disable rdiff specific tests if (BUILD_RDIFF) add_test(NAME rdiff_bad_option COMMAND ${WIN_BASH} rdiff_bad_option.test $ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests) add_test(NAME Help COMMAND ${WIN_BASH} help.test $ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests) add_test(NAME Mutate COMMAND ${WIN_BASH} mutate.test $ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests) add_test(NAME Signature COMMAND ${WIN_BASH} signature.test $ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests) add_test(NAME Sources COMMAND ${WIN_BASH} sources.test $ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests) add_test(NAME Triple COMMAND ${WIN_BASH} triple.test $ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests) add_test(NAME Delta COMMAND ${WIN_BASH} delta.test $ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests) add_test(NAME Changes COMMAND ${WIN_BASH} changes.test $ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests) endif (BUILD_RDIFF) # `make check` that will build everything and then run the tests. # See https://cmake.org/Wiki/CMakeEmulateMakeCheck and # https://github.com/librsync/librsync/issues/49 if (BUILD_RDIFF) set(LAST_TARGET rdiff) else (BUILD_RDIFF) set(LAST_TARGET rsync) endif (BUILD_RDIFF) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} -C Debug) add_dependencies(check ${LAST_TARGET} isprefix_test netint_test rollsum_test rabinkarp_test hashtable_test checksum_test sumset_test) enable_testing() # Create conf files configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/config.h) # We need to be able to #include "file" from a few places, # * The original source dir # * The generated source dir include_directories(${CMAKE_CURRENT_BINARY_DIR}/src) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) ########### next target ############### # Only list the .c files that need to be compiled # (Don't list .h files) set(rsync_LIB_SRCS src/prototab.c src/base64.c src/buf.c src/checksum.c src/command.c src/delta.c src/emit.c src/fileutil.c src/hashtable.c src/hex.c src/job.c src/mdfour.c src/mksum.c src/msg.c src/netint.c src/patch.c src/readsums.c src/rollsum.c src/rabinkarp.c src/scoop.c src/stats.c src/sumset.c src/trace.c src/tube.c src/util.c src/version.c src/whole.c ${blake2_SRCS}) add_library(rsync ${rsync_LIB_SRCS}) # TODO: Enable this when GenerateExportHeader works more widely. # include(GenerateExportHeader) # generate_export_header(rsync BASE_NAME librsync # EXPORT_FILE_NAME ${CMAKE_SOURCE_DIR}/src/librsync_export.h) target_link_libraries(rsync ${blake2_LIBS}) # Optionally link zlib and bzip2 if # - compression is enabled # - and libraries are found if (ENABLE_COMPRESSION) if (ZLIB_FOUND AND BZIP2_FOUND) target_link_libraries(rsync ${ZLIB_LIBRARIES} ${BZIP2_LIBRARIES}) else (ZLIB_FOUND AND BZIP2_FOUND) message (WARNING "zlib and bzip2 librares are required to enable compression") endif (ZLIB_FOUND AND BZIP2_FOUND) endif (ENABLE_COMPRESSION) # Set properties/options for shared vs static library. if (BUILD_SHARED_LIBS) set_target_properties(rsync PROPERTIES C_VISIBILITY_PRESET hidden) else (BUILD_SHARED_LIBS) target_compile_options(rsync PUBLIC -DLIBRSYNC_STATIC_DEFINE) endif (BUILD_SHARED_LIBS) set_target_properties(rsync PROPERTIES VERSION ${LIBRSYNC_VERSION} SOVERSION ${LIBRSYNC_MAJOR_VERSION}) install(TARGETS rsync ${INSTALL_TARGETS_DEFAULT_ARGS} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) ########### next target ############### if (BUILD_RDIFF) set(rdiff_SRCS src/rdiff.c src/isprefix.c) add_executable(rdiff ${rdiff_SRCS}) if (POPT_FOUND) target_link_libraries(rdiff rsync ${POPT_LIBRARIES}) else (POPT_FOUND) message (WARNING "Popt library is required for rdiff target") endif (POPT_FOUND) install(TARGETS rdiff ${INSTALL_TARGETS_DEFAULT_ARGS} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) endif (BUILD_RDIFF) ########### install files ############### install(FILES src/librsync.h src/librsync_export.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) message (STATUS "CMAKE_C_FLAGS = ${CMAKE_C_FLAGS}") install(FILES doc/librsync.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3) install(FILES doc/rdiff.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) # vim: shiftwidth=4 expandtab