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) 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(BROTLI_BUNDLED_MODE STREQUAL "") 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 OFF) 19 else() 20 set(BROTLI_BUNDLED_MODE ON) 21 endif() 22endif() 23mark_as_advanced(BROTLI_BUNDLED_MODE) 24 25include(CMakeDependentOption) 26CMAKE_DEPENDENT_OPTION(BUILD_SHARED_LIBS "Build shared libraries" ON "NOT BROTLI_BUNDLED_MODE" OFF) 27 28include(GNUInstallDirs) 29 30# When building shared libraries it is important to set the correct rpath. 31# See https://cmake.org/Wiki/CMake_RPATH_handling#Always_full_RPATH 32if (BUILD_SHARED_LIBS) 33 add_definitions(-DBROTLI_SHARED_COMPILATION) 34 set(CMAKE_SKIP_BUILD_RPATH FALSE) 35 set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 36 set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 37 list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_LIBDIR}" isSystemDir) 38 if ("${isSystemDir}" STREQUAL "-1") 39 set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_LIBDIR}") 40 endif() 41endif() 42 43# Parse version information from common/version.h. Normally we would 44# define these values here and write them out to configuration file(s) 45# (i.e., config.h), but in this case we parse them from 46# common/version.h to be less intrusive. 47function(hex_to_dec HEXADECIMAL DECIMAL) 48 string(TOUPPER "${HEXADECIMAL}" _tail) 49 set(_decimal 0) 50 string(LENGTH "${_tail}" _tail_length) 51 while (_tail_length GREATER 0) 52 math(EXPR _decimal "${_decimal} * 16") 53 string(SUBSTRING "${_tail}" 0 1 _digit) 54 string(SUBSTRING "${_tail}" 1 -1 _tail) 55 if (_digit STREQUAL "A") 56 math(EXPR _decimal "${_decimal} + 10") 57 elseif (_digit STREQUAL "B") 58 math(EXPR _decimal "${_decimal} + 11") 59 elseif (_digit STREQUAL "C") 60 math(EXPR _decimal "${_decimal} + 12") 61 elseif (_digit STREQUAL "D") 62 math(EXPR _decimal "${_decimal} + 13") 63 elseif (_digit STREQUAL "E") 64 math(EXPR _decimal "${_decimal} + 14") 65 elseif (_digit STREQUAL "F") 66 math(EXPR _decimal "${_decimal} + 15") 67 else() 68 math(EXPR _decimal "${_decimal} + ${_digit}") 69 endif() 70 string(LENGTH "${_tail}" _tail_length) 71 endwhile() 72 set(${DECIMAL} ${_decimal} PARENT_SCOPE) 73endfunction(hex_to_dec) 74 75# Version information 76file(STRINGS "common/version.h" _brotli_version_line REGEX "^#define BROTLI_VERSION (0x[0-9a-fA-F]+)$") 77string(REGEX REPLACE "^#define BROTLI_VERSION 0x([0-9a-fA-F]+)$" "\\1" _brotli_version_hex "${_brotli_version_line}") 78hex_to_dec("${_brotli_version_hex}" _brotli_version) 79math(EXPR BROTLI_VERSION_MAJOR "${_brotli_version} >> 24") 80math(EXPR BROTLI_VERSION_MINOR "(${_brotli_version} >> 12) & 4095") 81math(EXPR BROTLI_VERSION_REVISION "${_brotli_version} & 4095") 82mark_as_advanced(BROTLI_VERSION_MAJOR BROTLI_VERSION_MINOR BROTLI_VERSION_REVISION) 83 84if (ENABLE_SANITIZER) 85 set(CMAKE_C_FLAGS " ${CMAKE_C_FLAGS} -fsanitize=${ENABLE_SANITIZER}") 86 set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fsanitize=${ENABLE_SANITIZER}") 87 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=${ENABLE_SANITIZER}") 88 89 # By default, brotli depends on undefined behavior, but setting 90 # BROTLI_BUILD_PORTABLE should result in a build which does not. 91 if(ENABLE_SANITIZER STREQUAL "undefined") 92 add_definitions(-DBROTLI_BUILD_PORTABLE) 93 endif() 94endif () 95 96include(CheckFunctionExists) 97set(LIBM_LIBRARY) 98CHECK_FUNCTION_EXISTS(log2 LOG2_RES) 99if(NOT LOG2_RES) 100 set(orig_req_libs "${CMAKE_REQUIRED_LIBRARIES}") 101 set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m") 102 CHECK_FUNCTION_EXISTS(log2 LOG2_LIBM_RES) 103 if(LOG2_LIBM_RES) 104 set(LIBM_LIBRARY "m") 105 else() 106 message(FATAL_ERROR "log2() not found") 107 endif() 108 109 set(CMAKE_REQUIRED_LIBRARIES "${orig_req_libs}") 110 unset(LOG2_LIBM_RES) 111 unset(orig_req_libs) 112endif() 113unset(LOG2_RES) 114 115set(BROTLI_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include") 116set(BROTLI_LIBRARIES_CORE brotlienc brotlidec brotlicommon) 117set(BROTLI_LIBRARIES ${BROTLI_LIBRARIES_CORE} ${LIBM_LIBRARY}) 118mark_as_advanced(BROTLI_INCLUDE_DIRS BROTLI_LIBRARIES) 119 120if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 121 add_definitions(-DOS_LINUX) 122elseif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") 123 add_definitions(-DOS_FREEBSD) 124elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 125 add_definitions(-DOS_MACOSX) 126endif() 127 128add_library(brotlicommon 129 common/dictionary.c) 130add_library(brotlidec 131 dec/bit_reader.c 132 dec/decode.c 133 dec/huffman.c 134 dec/state.c) 135add_library(brotlienc 136 enc/backward_references.c 137 enc/backward_references_hq.c 138 enc/bit_cost.c 139 enc/block_splitter.c 140 enc/brotli_bit_stream.c 141 enc/cluster.c 142 enc/compress_fragment.c 143 enc/compress_fragment_two_pass.c 144 enc/dictionary_hash.c 145 enc/encode.c 146 enc/entropy_encode.c 147 enc/histogram.c 148 enc/literal_cost.c 149 enc/memory.c 150 enc/metablock.c 151 enc/static_dict.c 152 enc/utf8_util.c) 153 154# Older CMake versions does not understand INCLUDE_DIRECTORIES property. 155include_directories(${BROTLI_INCLUDE_DIRS}) 156 157foreach(lib brotlicommon brotlidec brotlienc) 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 SOVERSION "${BROTLI_VERSION_MAJOR}.${BROTLI_VERSION_MINOR}.${BROTLI_VERSION_REVISION}" 162 VERSION "${BROTLI_VERSION_MAJOR}.${BROTLI_VERSION_MINOR}.${BROTLI_VERSION_REVISION}" 163 POSITION_INDEPENDENT_CODE TRUE) 164 string(TOUPPER "${lib}" LIB) 165 set_target_properties (${lib} PROPERTIES DEFINE_SYMBOL "${LIB}_SHARED_COMPILATION" ) 166 167 set_property(TARGET ${lib} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${BROTLI_INCLUDE_DIRS}") 168endforeach() 169 170target_link_libraries(brotlidec brotlicommon) 171target_link_libraries(brotlienc brotlicommon) 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 bro executable 185add_executable(bro tools/bro.c) 186target_link_libraries(bro ${BROTLI_LIBRARIES}) 187 188# Installation 189if(NOT BROTLI_BUNDLED_MODE) 190 install (TARGETS bro RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") 191 192 if(WIN32) 193 install( 194 TARGETS ${BROTLI_LIBRARIES_CORE} 195 LIBRARY DESTINATION "${CMAKE_INSTALL_BINDIR}" 196 ARCHIVE DESTINATION "${CMAKE_INSTALL_BINDIR}" 197 ) 198 else() 199 install(TARGETS ${BROTLI_LIBRARIES_CORE} 200 LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 201 ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" 202 ) 203 install(DIRECTORY ${BROTLI_INCLUDE_DIRS}/brotli DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") 204 endif() 205 206endif() 207 208# Tests 209 210# If we're targeting Windows but not running on Windows, we need Wine 211# to run the tests... 212if(NOT BROTLI_DISABLE_TESTS) 213 if(WIN32 AND NOT CMAKE_HOST_WIN32) 214 find_program(BROTLI_WINE NAMES wine) 215 216 if(NOT BROTLI_WINE) 217 message(STATUS "wine not found, disabling tests") 218 set(BROTLI_DISABLE_TESTS TRUE) 219 endif() 220 endif() 221endif() 222 223if(NOT BROTLI_DISABLE_TESTS) 224 include(CTest) 225 enable_testing() 226 227 set(ROUNDTRIP_INPUTS 228 tests/testdata/alice29.txt 229 tests/testdata/asyoulik.txt 230 tests/testdata/lcet10.txt 231 tests/testdata/plrabn12.txt 232 enc/encode.c 233 common/dictionary.h 234 dec/decode.c) 235 236 foreach(INPUT ${ROUNDTRIP_INPUTS}) 237 get_filename_component(OUTPUT_NAME "${INPUT}" NAME) 238 239 set(OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_NAME}") 240 set(INPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}") 241 242 foreach(quality 1 6 9 11) 243 add_test(NAME "${BROTLI_TEST_PREFIX}roundtrip/${INPUT}/${quality}" 244 COMMAND "${CMAKE_COMMAND}" 245 -DBROTLI_WRAPPER=${BROTLI_WINE} 246 -DBROTLI_CLI=$<TARGET_FILE:bro> 247 -DQUALITY=${quality} 248 -DINPUT=${INPUT_FILE} 249 -DOUTPUT=${OUTPUT_FILE}.${quality} 250 -P ${CMAKE_CURRENT_SOURCE_DIR}/tests/run-roundtrip-test.cmake) 251 endforeach() 252 endforeach() 253 254 file(GLOB_RECURSE 255 COMPATIBILITY_INPUTS 256 RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} 257 tests/testdata/*.compressed*) 258 259 foreach(INPUT ${COMPATIBILITY_INPUTS}) 260 add_test(NAME "${BROTLI_TEST_PREFIX}compatibility/${INPUT}" 261 COMMAND "${CMAKE_COMMAND}" 262 -DBROTLI_WRAPPER=${BROTLI_WINE} 263 -DBROTLI_CLI=$<TARGET_FILE:bro> 264 -DINPUT=${CMAKE_CURRENT_SOURCE_DIR}/${INPUT} 265 -P ${CMAKE_CURRENT_SOURCE_DIR}/tests/run-compatibility-test.cmake) 266 endforeach() 267endif() 268 269# Generate a pkg-config file 270 271include(CMakeParseArguments) 272 273function(generate_pkg_config_path outvar path) 274 string(LENGTH "${path}" path_length) 275 276 set(path_args ${ARGV}) 277 list(REMOVE_AT path_args 0 1) 278 list(LENGTH path_args path_args_remaining) 279 280 set("${outvar}" "${path}") 281 282 while(path_args_remaining GREATER 1) 283 list(GET path_args 0 name) 284 list(GET path_args 1 value) 285 286 get_filename_component(value_full "${value}" ABSOLUTE) 287 string(LENGTH "${value}" value_length) 288 289 if(path_length EQUAL value_length AND path STREQUAL value) 290 set("${outvar}" "\${${name}}") 291 break() 292 elseif(path_length GREATER value_length) 293 # We might be in a subdirectory of the value, but we have to be 294 # careful about a prefix matching but not being a subdirectory 295 # (for example, /usr/lib64 is not a subdirectory of /usr/lib). 296 # We'll do this by making sure the next character is a directory 297 # separator. 298 string(SUBSTRING "${path}" ${value_length} 1 sep) 299 if(sep STREQUAL "/") 300 string(SUBSTRING "${path}" 0 ${value_length} s) 301 if(s STREQUAL value) 302 string(SUBSTRING "${path}" "${value_length}" -1 suffix) 303 set("${outvar}" "\${${name}}${suffix}") 304 break() 305 endif() 306 endif() 307 endif() 308 309 list(REMOVE_AT path_args 0 1) 310 list(LENGTH path_args path_args_remaining) 311 endwhile() 312 313 set("${outvar}" "${${outvar}}" PARENT_SCOPE) 314endfunction(generate_pkg_config_path) 315 316function(generate_pkg_config output_file) 317 set (options) 318 set (oneValueArgs NAME DESCRIPTION URL VERSION PREFIX LIBDIR INCLUDEDIR) 319 set (multiValueArgs DEPENDS_PRIVATE CFLAGS LIBRARIES) 320 cmake_parse_arguments(GEN_PKG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 321 unset (options) 322 unset (oneValueArgs) 323 unset (multiValueArgs) 324 325 if(NOT GEN_PKG_PREFIX) 326 set(GEN_PKG_PREFIX "${CMAKE_INSTALL_PREFIX}") 327 endif() 328 329 if(NOT GEN_PKG_LIBDIR) 330 set(GEN_PKG_LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}") 331 endif() 332 generate_pkg_config_path(GEN_PKG_LIBDIR "${GEN_PKG_LIBDIR}" 333 prefix "${GEN_PKG_PREFIX}") 334 335 if(NOT GEN_PKG_INCLUDEDIR) 336 set(GEN_PKG_INCLUDEDIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}") 337 endif() 338 generate_pkg_config_path(GEN_PKG_INCLUDEDIR "${GEN_PKG_INCLUDEDIR}" 339 prefix "${GEN_PKG_PREFIX}") 340 341 file(WRITE "${output_file}" "prefix=${GEN_PKG_PREFIX}\n") 342 file(APPEND "${output_file}" "libdir=${GEN_PKG_LIBDIR}\n") 343 file(APPEND "${output_file}" "includedir=${GEN_PKG_INCLUDEDIR}\n") 344 file(APPEND "${output_file}" "\n") 345 346 if(GEN_PKG_NAME) 347 file(APPEND "${output_file}" "Name: ${GEN_PKG_NAME}\n") 348 else() 349 file(APPEND "${output_file}" "Name: ${CMAKE_PROJECT_NAME}\n") 350 endif() 351 352 if(GEN_PKG_DESCRIPTION) 353 file(APPEND "${output_file}" "Description: ${GEN_PKG_DESCRIPTION}\n") 354 endif() 355 356 if(GEN_PKG_URL) 357 file(APPEND "${output_file}" "URL: ${GEN_PKG_URL}\n") 358 endif() 359 360 if(GEN_PKG_VERSION) 361 file(APPEND "${output_file}" "Version: ${GEN_PKG_VERSION}\n") 362 endif() 363 364 if(GEN_PKG_DEPENDS_PRIVATE) 365 file(APPEND "${output_file}" "Requires.private:") 366 foreach(lib ${GEN_PKG_DEPENDS_PRIVATE}) 367 file(APPEND "${output_file}" " ${lib}") 368 endforeach() 369 file(APPEND "${output_file}" "\n") 370 endif() 371 372 if(GEN_PKG_LIBRARIES) 373 set(libs) 374 375 file(APPEND "${output_file}" "Libs: -L\${libdir}") 376 foreach(lib ${GEN_PKG_LIBRARIES}) 377 file(APPEND "${output_file}" " -l${lib}") 378 endforeach() 379 file(APPEND "${output_file}" "\n") 380 endif() 381 382 file(APPEND "${output_file}" "Cflags: -I\${includedir}") 383 if(GEN_PKG_CFLAGS) 384 foreach(cflag ${GEN_PKG_CFLAGS}) 385 file(APPEND "${output_file}" " ${cflag}") 386 endforeach() 387 endif() 388 file(APPEND "${output_file}" "\n") 389endfunction(generate_pkg_config) 390 391generate_pkg_config ("${CMAKE_CURRENT_BINARY_DIR}/libbrotlicommon.pc" 392 NAME libbrotlicommon 393 DESCRIPTION "Shared data used by libbrotlienc and libbrotlidec libraries" 394 URL "https://github.com/google/brotli" 395 VERSION "${BROTLI_VERSION_MAJOR}.${BROTLI_VERSION_MINOR}.${BROTLI_VERSION_REVISION}" 396 LIBRARIES brotlicommon) 397 398generate_pkg_config ("${CMAKE_CURRENT_BINARY_DIR}/libbrotlidec.pc" 399 NAME libbrotlidec 400 DESCRIPTION "Brotli decoder library" 401 VERSION "${BROTLI_VERSION_MAJOR}.${BROTLI_VERSION_MINOR}.${BROTLI_VERSION_REVISION}" 402 URL "https://github.com/google/brotli" 403 DEPENDS_PRIVATE libbrotlicommon 404 LIBRARIES brotlidec) 405 406generate_pkg_config ("${CMAKE_CURRENT_BINARY_DIR}/libbrotlienc.pc" 407 NAME libbrotlienc 408 DESCRIPTION "Brotli encoder library" 409 VERSION "${BROTLI_VERSION_MAJOR}.${BROTLI_VERSION_MINOR}.${BROTLI_VERSION_REVISION}" 410 URL "https://github.com/google/brotli" 411 DEPENDS_PRIVATE libbrotlicommon 412 LIBRARIES brotlienc) 413 414if(NOT BROTLI_BUNDLED_MODE) 415 install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbrotlicommon.pc" 416 DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 417 install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbrotlidec.pc" 418 DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 419 install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbrotlienc.pc" 420 DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 421endif() 422 423if (ENABLE_COVERAGE STREQUAL "yes") 424 SETUP_TARGET_FOR_COVERAGE(coverage test coverage) 425endif () 426