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