1# Ubuntu 12.04 LTS has CMake 2.8.7, and is an important target since 2# several CI services, such as Travis and Drone, use it. Solaris 11 3# has 2.8.6, and it's not difficult to support if you already have to 4# support 2.8.7. 5cmake_minimum_required(VERSION 2.8.6) 6 7project(brotli C) 8 9if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 10 message(STATUS "Setting build type to Release as none was specified.") 11 set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE) 12else() 13 message(STATUS "Build type is '${CMAKE_BUILD_TYPE}'") 14endif() 15 16include(CheckCSourceCompiles) 17check_c_source_compiles( 18 "#if defined(__EMSCRIPTEN__) 19 int main() {return 0;} 20 #endif" 21 BROTLI_EMSCRIPTEN 22) 23if (BROTLI_EMSCRIPTEN) 24 message("-- Compiler is EMSCRIPTEN") 25else() 26 message("-- Compiler is not EMSCRIPTEN") 27endif() 28 29# If Brotli is being bundled in another project, we don't want to 30# install anything. However, we want to let people override this, so 31# we'll use the BROTLI_BUNDLED_MODE variable to let them do that; just 32# set it to OFF in your project before you add_subdirectory(brotli). 33get_directory_property(BROTLI_PARENT_DIRECTORY PARENT_DIRECTORY) 34if(NOT DEFINED BROTLI_BUNDLED_MODE) 35 # Bundled mode hasn't been set one way or the other, set the default 36 # depending on whether or not we are the top-level project. 37 if(BROTLI_PARENT_DIRECTORY) 38 set(BROTLI_BUNDLED_MODE ON) 39 else() 40 set(BROTLI_BUNDLED_MODE OFF) 41 endif() 42endif() 43mark_as_advanced(BROTLI_BUNDLED_MODE) 44 45include(GNUInstallDirs) 46 47# Parse version information from common/version.h. Normally we would 48# define these values here and write them out to configuration file(s) 49# (i.e., config.h), but in this case we parse them from 50# common/version.h to be less intrusive. 51function(hex_to_dec HEXADECIMAL DECIMAL) 52 string(TOUPPER "${HEXADECIMAL}" _tail) 53 set(_decimal 0) 54 string(LENGTH "${_tail}" _tail_length) 55 while (_tail_length GREATER 0) 56 math(EXPR _decimal "${_decimal} * 16") 57 string(SUBSTRING "${_tail}" 0 1 _digit) 58 string(SUBSTRING "${_tail}" 1 -1 _tail) 59 if (_digit STREQUAL "A") 60 math(EXPR _decimal "${_decimal} + 10") 61 elseif (_digit STREQUAL "B") 62 math(EXPR _decimal "${_decimal} + 11") 63 elseif (_digit STREQUAL "C") 64 math(EXPR _decimal "${_decimal} + 12") 65 elseif (_digit STREQUAL "D") 66 math(EXPR _decimal "${_decimal} + 13") 67 elseif (_digit STREQUAL "E") 68 math(EXPR _decimal "${_decimal} + 14") 69 elseif (_digit STREQUAL "F") 70 math(EXPR _decimal "${_decimal} + 15") 71 else() 72 math(EXPR _decimal "${_decimal} + ${_digit}") 73 endif() 74 string(LENGTH "${_tail}" _tail_length) 75 endwhile() 76 set(${DECIMAL} ${_decimal} PARENT_SCOPE) 77endfunction(hex_to_dec) 78 79# Version information 80file(STRINGS "c/common/version.h" _brotli_version_line REGEX "^#define BROTLI_VERSION (0x[0-9a-fA-F]+)$") 81string(REGEX REPLACE "^#define BROTLI_VERSION 0x([0-9a-fA-F]+)$" "\\1" _brotli_version_hex "${_brotli_version_line}") 82hex_to_dec("${_brotli_version_hex}" _brotli_version) 83math(EXPR BROTLI_VERSION_MAJOR "${_brotli_version} >> 24") 84math(EXPR BROTLI_VERSION_MINOR "(${_brotli_version} >> 12) & 4095") 85math(EXPR BROTLI_VERSION_PATCH "${_brotli_version} & 4095") 86set(BROTLI_VERSION "${BROTLI_VERSION_MAJOR}.${BROTLI_VERSION_MINOR}.${BROTLI_VERSION_PATCH}") 87mark_as_advanced(BROTLI_VERSION BROTLI_VERSION_MAJOR BROTLI_VERSION_MINOR BROTLI_VERSION_PATCH) 88 89# ABI Version information 90file(STRINGS "c/common/version.h" _brotli_abi_info_line REGEX "^#define BROTLI_ABI_VERSION (0x[0-9a-fA-F]+)$") 91string(REGEX REPLACE "^#define BROTLI_ABI_VERSION 0x([0-9a-fA-F]+)$" "\\1" _brotli_abi_info_hex "${_brotli_abi_info_line}") 92hex_to_dec("${_brotli_abi_info_hex}" _brotli_abi_info) 93math(EXPR BROTLI_ABI_CURRENT "${_brotli_abi_info} >> 24") 94math(EXPR BROTLI_ABI_REVISION "(${_brotli_abi_info} >> 12) & 4095") 95math(EXPR BROTLI_ABI_AGE "${_brotli_abi_info} & 4095") 96math(EXPR BROTLI_ABI_COMPATIBILITY "${BROTLI_ABI_CURRENT} - ${BROTLI_ABI_AGE}") 97mark_as_advanced(BROTLI_ABI_CURRENT BROTLI_ABI_REVISION BROTLI_ABI_AGE BROTLI_ABI_COMPATIBILITY) 98 99if (ENABLE_SANITIZER) 100 set(CMAKE_C_FLAGS " ${CMAKE_C_FLAGS} -fsanitize=${ENABLE_SANITIZER}") 101 set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fsanitize=${ENABLE_SANITIZER}") 102 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=${ENABLE_SANITIZER}") 103 104 # By default, brotli depends on undefined behavior, but setting 105 # BROTLI_BUILD_PORTABLE should result in a build which does not. 106 if(ENABLE_SANITIZER STREQUAL "undefined") 107 add_definitions(-DBROTLI_BUILD_PORTABLE) 108 endif() 109endif () 110 111include(CheckFunctionExists) 112set(LIBM_LIBRARY) 113CHECK_FUNCTION_EXISTS(log2 LOG2_RES) 114if(NOT LOG2_RES) 115 set(orig_req_libs "${CMAKE_REQUIRED_LIBRARIES}") 116 set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m") 117 CHECK_FUNCTION_EXISTS(log2 LOG2_LIBM_RES) 118 if(LOG2_LIBM_RES) 119 set(LIBM_LIBRARY "m") 120 add_definitions(-DBROTLI_HAVE_LOG2=1) 121 else() 122 add_definitions(-DBROTLI_HAVE_LOG2=0) 123 endif() 124 125 set(CMAKE_REQUIRED_LIBRARIES "${orig_req_libs}") 126 unset(LOG2_LIBM_RES) 127 unset(orig_req_libs) 128else() 129 add_definitions(-DBROTLI_HAVE_LOG2=1) 130endif() 131unset(LOG2_RES) 132 133set(BROTLI_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/c/include") 134mark_as_advanced(BROTLI_INCLUDE_DIRS) 135 136set(BROTLI_LIBRARIES_CORE brotlienc brotlidec brotlicommon) 137set(BROTLI_LIBRARIES ${BROTLI_LIBRARIES_CORE} ${LIBM_LIBRARY}) 138mark_as_advanced(BROTLI_LIBRARIES) 139 140set(BROTLI_LIBRARIES_CORE_STATIC brotlienc-static brotlidec-static brotlicommon-static) 141set(BROTLI_LIBRARIES_STATIC ${BROTLI_LIBRARIES_CORE_STATIC} ${LIBM_LIBRARY}) 142mark_as_advanced(BROTLI_LIBRARIES_STATIC) 143 144if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 145 add_definitions(-DOS_LINUX) 146elseif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") 147 add_definitions(-DOS_FREEBSD) 148elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 149 add_definitions(-DOS_MACOSX) 150endif() 151 152function(transform_sources_list INPUT_FILE OUTPUT_FILE) 153 file(READ ${INPUT_FILE} TEXT) 154 string(REGEX REPLACE "\\\\\n" "~continuation~" TEXT ${TEXT}) 155 string(REGEX REPLACE "([a-zA-Z_][a-zA-Z0-9_]*)[\t ]*=[\t ]*([^\n]*)" "SET(\\1 \\2)" TEXT ${TEXT}) 156 string(REPLACE "~continuation~" "\n" TEXT ${TEXT}) 157 file(WRITE ${OUTPUT_FILE} ${TEXT}) 158endfunction() 159 160transform_sources_list("scripts/sources.lst" "${CMAKE_CURRENT_BINARY_DIR}/sources.lst.cmake") 161include("${CMAKE_CURRENT_BINARY_DIR}/sources.lst.cmake") 162 163if(BROTLI_EMSCRIPTEN) 164 set(BROTLI_SHARED_LIBS "") 165else() 166 set(BROTLI_SHARED_LIBS brotlicommon brotlidec brotlienc) 167 add_library(brotlicommon SHARED ${BROTLI_COMMON_C}) 168 add_library(brotlidec SHARED ${BROTLI_DEC_C}) 169 add_library(brotlienc SHARED ${BROTLI_ENC_C}) 170endif() 171 172set(BROTLI_STATIC_LIBS brotlicommon-static brotlidec-static brotlienc-static) 173add_library(brotlicommon-static STATIC ${BROTLI_COMMON_C}) 174add_library(brotlidec-static STATIC ${BROTLI_DEC_C}) 175add_library(brotlienc-static STATIC ${BROTLI_ENC_C}) 176 177# Older CMake versions does not understand INCLUDE_DIRECTORIES property. 178include_directories(${BROTLI_INCLUDE_DIRS}) 179 180foreach(lib IN LISTS BROTLI_SHARED_LIBS) 181 target_compile_definitions(${lib} PUBLIC "BROTLI_SHARED_COMPILATION" ) 182 string(TOUPPER "${lib}" LIB) 183 set_target_properties (${lib} PROPERTIES DEFINE_SYMBOL "${LIB}_SHARED_COMPILATION") 184endforeach() 185 186foreach(lib IN LISTS BROTLI_SHARED_LIBS BROTLI_STATIC_LIBS) 187 target_link_libraries(${lib} ${LIBM_LIBRARY}) 188 set_property(TARGET ${lib} APPEND PROPERTY INCLUDE_DIRECTORIES ${BROTLI_INCLUDE_DIRS}) 189 set_target_properties(${lib} PROPERTIES 190 VERSION "${BROTLI_ABI_COMPATIBILITY}.${BROTLI_ABI_AGE}.${BROTLI_ABI_REVISION}" 191 SOVERSION "${BROTLI_ABI_COMPATIBILITY}") 192 if(NOT BROTLI_EMSCRIPTEN) 193 set_target_properties(${lib} PROPERTIES POSITION_INDEPENDENT_CODE TRUE) 194 endif() 195 set_property(TARGET ${lib} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${BROTLI_INCLUDE_DIRS}") 196endforeach() 197 198if(NOT BROTLI_EMSCRIPTEN) 199target_link_libraries(brotlidec brotlicommon) 200target_link_libraries(brotlienc brotlicommon) 201endif() 202 203target_link_libraries(brotlidec-static brotlicommon-static) 204target_link_libraries(brotlienc-static brotlicommon-static) 205 206# For projects stuck on older versions of CMake, this will set the 207# BROTLI_INCLUDE_DIRS and BROTLI_LIBRARIES variables so they still 208# have a relatively easy way to use Brotli: 209# 210# include_directories(${BROTLI_INCLUDE_DIRS}) 211# target_link_libraries(foo ${BROTLI_LIBRARIES}) 212if(BROTLI_PARENT_DIRECTORY) 213 set(BROTLI_INCLUDE_DIRS "${BROTLI_INCLUDE_DIRS}" PARENT_SCOPE) 214 set(BROTLI_LIBRARIES "${BROTLI_LIBRARIES}" PARENT_SCOPE) 215endif() 216 217# Build the brotli executable 218add_executable(brotli ${BROTLI_CLI_C}) 219target_link_libraries(brotli ${BROTLI_LIBRARIES_STATIC}) 220 221# Installation 222if(NOT BROTLI_EMSCRIPTEN) 223if(NOT BROTLI_BUNDLED_MODE) 224 install( 225 TARGETS brotli 226 RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 227 ) 228 229 install( 230 TARGETS ${BROTLI_LIBRARIES_CORE} 231 ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" 232 LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 233 RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 234 ) 235 236 install( 237 TARGETS ${BROTLI_LIBRARIES_CORE_STATIC} 238 ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" 239 LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 240 RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 241 ) 242 243 install( 244 DIRECTORY ${BROTLI_INCLUDE_DIRS}/brotli 245 DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 246 ) 247endif() # BROTLI_BUNDLED_MODE 248endif() # BROTLI_EMSCRIPTEN 249 250# Tests 251 252# If we're targeting Windows but not running on Windows, we need Wine 253# to run the tests... 254if(NOT BROTLI_DISABLE_TESTS) 255 if(WIN32 AND NOT CMAKE_HOST_WIN32) 256 find_program(BROTLI_WRAPPER NAMES wine) 257 258 if(NOT BROTLI_WRAPPER) 259 message(STATUS "wine not found, disabling tests") 260 set(BROTLI_DISABLE_TESTS TRUE) 261 endif() 262 endif() 263endif() 264 265# If our compiler is a cross-compiler that we know about (arm/aarch64), 266# then we need to use qemu to execute the tests. 267if(NOT BROTLI_DISABLE_TESTS) 268 if ("${CMAKE_C_COMPILER}" MATCHES "^.*/arm-linux-gnueabihf-.*$") 269 message(STATUS "Detected arm-linux-gnueabihf cross-compilation") 270 set(BROTLI_WRAPPER "qemu-arm") 271 set(BROTLI_WRAPPER_LD_PREFIX "/usr/arm-linux-gnueabihf") 272 endif() 273 274 if ("${CMAKE_C_COMPILER}" MATCHES "^.*/arm-linux-gnueabi-.*$") 275 message(STATUS "Detected arm-linux-gnueabi cross-compilation") 276 set(BROTLI_WRAPPER "qemu-arm") 277 set(BROTLI_WRAPPER_LD_PREFIX "/usr/arm-linux-gnueabi") 278 endif() 279 280 if ("${CMAKE_C_COMPILER}" MATCHES "^.*/aarch64-linux-gnu-.*$") 281 message(STATUS "Detected aarch64-linux-gnu cross-compilation") 282 set(BROTLI_WRAPPER "qemu-aarch64") 283 set(BROTLI_WRAPPER_LD_PREFIX "/usr/aarch64-linux-gnu") 284 endif() 285endif() 286 287if(NOT BROTLI_DISABLE_TESTS) 288 include(CTest) 289 enable_testing() 290 291 set(ROUNDTRIP_INPUTS 292 tests/testdata/alice29.txt 293 tests/testdata/asyoulik.txt 294 tests/testdata/lcet10.txt 295 tests/testdata/plrabn12.txt 296 c/enc/encode.c 297 c/common/dictionary.h 298 c/dec/decode.c) 299 300 foreach(INPUT ${ROUNDTRIP_INPUTS}) 301 get_filename_component(OUTPUT_NAME "${INPUT}" NAME) 302 303 set(OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_NAME}") 304 set(INPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}") 305 306 if (EXISTS "${INPUT_FILE}") 307 foreach(quality 1 6 9 11) 308 add_test(NAME "${BROTLI_TEST_PREFIX}roundtrip/${INPUT}/${quality}" 309 COMMAND "${CMAKE_COMMAND}" 310 -DBROTLI_WRAPPER=${BROTLI_WRAPPER} 311 -DBROTLI_WRAPPER_LD_PREFIX=${BROTLI_WRAPPER_LD_PREFIX} 312 -DBROTLI_CLI=$<TARGET_FILE:brotli> 313 -DQUALITY=${quality} 314 -DINPUT=${INPUT_FILE} 315 -DOUTPUT=${OUTPUT_FILE}.${quality} 316 -P ${CMAKE_CURRENT_SOURCE_DIR}/tests/run-roundtrip-test.cmake) 317 endforeach() 318 else() 319 message(WARNING "Test file ${INPUT} does not exist.") 320 endif() 321 endforeach() 322 323 file(GLOB_RECURSE 324 COMPATIBILITY_INPUTS 325 RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} 326 tests/testdata/*.compressed*) 327 328 foreach(INPUT ${COMPATIBILITY_INPUTS}) 329 add_test(NAME "${BROTLI_TEST_PREFIX}compatibility/${INPUT}" 330 COMMAND "${CMAKE_COMMAND}" 331 -DBROTLI_WRAPPER=${BROTLI_WRAPPER} 332 -DBROTLI_WRAPPER_LD_PREFIX=${BROTLI_WRAPPER_LD_PREFIX} 333 -DBROTLI_CLI=$<TARGET_FILE:brotli> 334 -DINPUT=${CMAKE_CURRENT_SOURCE_DIR}/${INPUT} 335 -P ${CMAKE_CURRENT_SOURCE_DIR}/tests/run-compatibility-test.cmake) 336 endforeach() 337endif() 338 339# Generate a pkg-config files 340 341function(generate_pkg_config_path outvar path) 342 string(LENGTH "${path}" path_length) 343 344 set(path_args ${ARGV}) 345 list(REMOVE_AT path_args 0 1) 346 list(LENGTH path_args path_args_remaining) 347 348 set("${outvar}" "${path}") 349 350 while(path_args_remaining GREATER 1) 351 list(GET path_args 0 name) 352 list(GET path_args 1 value) 353 354 get_filename_component(value_full "${value}" ABSOLUTE) 355 string(LENGTH "${value}" value_length) 356 357 if(path_length EQUAL value_length AND path STREQUAL value) 358 set("${outvar}" "\${${name}}") 359 break() 360 elseif(path_length GREATER value_length) 361 # We might be in a subdirectory of the value, but we have to be 362 # careful about a prefix matching but not being a subdirectory 363 # (for example, /usr/lib64 is not a subdirectory of /usr/lib). 364 # We'll do this by making sure the next character is a directory 365 # separator. 366 string(SUBSTRING "${path}" ${value_length} 1 sep) 367 if(sep STREQUAL "/") 368 string(SUBSTRING "${path}" 0 ${value_length} s) 369 if(s STREQUAL value) 370 string(SUBSTRING "${path}" "${value_length}" -1 suffix) 371 set("${outvar}" "\${${name}}${suffix}") 372 break() 373 endif() 374 endif() 375 endif() 376 377 list(REMOVE_AT path_args 0 1) 378 list(LENGTH path_args path_args_remaining) 379 endwhile() 380 381 set("${outvar}" "${${outvar}}" PARENT_SCOPE) 382endfunction(generate_pkg_config_path) 383 384function(transform_pc_file INPUT_FILE OUTPUT_FILE VERSION) 385 file(READ ${INPUT_FILE} TEXT) 386 387 set(PREFIX "${CMAKE_INSTALL_PREFIX}") 388 string(REGEX REPLACE "@prefix@" "${PREFIX}" TEXT ${TEXT}) 389 string(REGEX REPLACE "@exec_prefix@" "${PREFIX}" TEXT ${TEXT}) 390 391 generate_pkg_config_path(LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}" prefix "${PREFIX}") 392 string(REGEX REPLACE "@libdir@" "${LIBDIR}" TEXT ${TEXT}) 393 394 generate_pkg_config_path(INCLUDEDIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}" prefix "${PREFIX}") 395 string(REGEX REPLACE "@includedir@" "${INCLUDEDIR}" TEXT ${TEXT}) 396 397 string(REGEX REPLACE "@PACKAGE_VERSION@" "${VERSION}" TEXT ${TEXT}) 398 399 file(WRITE ${OUTPUT_FILE} ${TEXT}) 400endfunction() 401 402transform_pc_file("scripts/libbrotlicommon.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libbrotlicommon.pc" "${BROTLI_VERSION}") 403 404transform_pc_file("scripts/libbrotlidec.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libbrotlidec.pc" "${BROTLI_VERSION}") 405 406transform_pc_file("scripts/libbrotlienc.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libbrotlienc.pc" "${BROTLI_VERSION}") 407 408if(NOT BROTLI_EMSCRIPTEN) 409if(NOT BROTLI_BUNDLED_MODE) 410 install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbrotlicommon.pc" 411 DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 412 install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbrotlidec.pc" 413 DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 414 install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbrotlienc.pc" 415 DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 416endif() # BROTLI_BUNDLED_MODE 417endif() # BROTLI_EMSCRIPTEN 418 419if (ENABLE_COVERAGE STREQUAL "yes") 420 SETUP_TARGET_FOR_COVERAGE(coverage test coverage) 421endif () 422