• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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