Edit

kc3-lang/brotli/CMakeLists.txt

Branch :

  • CMakeLists.txt
  • # Available CMake versions:
    #  - Ubuntu  20.04 LTS   : 3.16.3
    #  - Solaris 11.4 SRU 15 : 3.15
    cmake_minimum_required(VERSION 3.15)
    
    # Since this project's version is loaded from other files, this policy
    # will help suppress the warning generated by cmake.
    # This policy is set because we can't provide "VERSION" in "project" command.
    # Use `cmake --help-policy CMP0048` for more information.
    cmake_policy(SET CMP0048 NEW)
    project(brotli C)
    
    if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
      message(STATUS "Setting build type to Release as none was specified")
      set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build" FORCE)
    else()
      message(STATUS "Build type is '${CMAKE_BUILD_TYPE}'")
    endif()
    
    include(CheckCSourceCompiles)
    check_c_source_compiles(
      "#if defined(__EMSCRIPTEN__)
       int main() {return 0;}
       #endif"
      BROTLI_EMSCRIPTEN
    )
    if (BROTLI_EMSCRIPTEN)
      message("-- Compiler is EMSCRIPTEN")
    else()
      message("-- Compiler is not EMSCRIPTEN")
    endif()
    
    if (BROTLI_EMSCRIPTEN)
      message(STATUS "Switching to static build for EMSCRIPTEN")
      set(BUILD_SHARED_LIBS OFF)
    endif()
    
    # Reflect CMake variable as a build option.
    option(BUILD_SHARED_LIBS "Build shared libraries" ON)
    set(BROTLI_BUILD_TOOLS ON CACHE BOOL "Build/install CLI tools")
    set(BROTLI_BUILD_FOR_PACKAGE OFF CACHE BOOL "Build/install both shared and static libraries")
    
    if (BROTLI_BUILD_FOR_PACKAGE AND NOT BUILD_SHARED_LIBS)
      message(FATAL_ERROR "Both BROTLI_BUILD_FOR_PACKAGE and BUILD_SHARED_LIBS are set")
    endif()
    
    # If Brotli is being bundled in another project, we don't want to
    # install anything.  However, we want to let people override this, so
    # we'll use the BROTLI_BUNDLED_MODE variable to let them do that; just
    # set it to OFF in your project before you add_subdirectory(brotli).
    get_directory_property(BROTLI_PARENT_DIRECTORY PARENT_DIRECTORY)
    if (NOT DEFINED BROTLI_BUNDLED_MODE)
      # Bundled mode hasn't been set one way or the other, set the default
      # depending on whether or not we are the top-level project.
      if (BROTLI_PARENT_DIRECTORY)
        set(BROTLI_BUNDLED_MODE ON)
      else()
        set(BROTLI_BUNDLED_MODE OFF)
      endif()
    endif()  # BROTLI_BUNDLED_MODE
    mark_as_advanced(BROTLI_BUNDLED_MODE)
    
    include(GNUInstallDirs)
    
    # Reads macro from .h file; it is expected to be a single-line define.
    function(read_macro PATH MACRO OUTPUT)
      file(STRINGS ${PATH} _line REGEX "^#define +${MACRO} +(.+)$")
      string(REGEX REPLACE "^#define +${MACRO} +(.+)$" "\\1" _val "${_line}")
      set(${OUTPUT} ${_val} PARENT_SCOPE)
    endfunction(read_macro)
    
    # Version information
    read_macro("c/common/version.h" "BROTLI_VERSION_MAJOR" BROTLI_VERSION_MAJOR)
    read_macro("c/common/version.h" "BROTLI_VERSION_MINOR" BROTLI_VERSION_MINOR)
    read_macro("c/common/version.h" "BROTLI_VERSION_PATCH" BROTLI_VERSION_PATCH)
    set(BROTLI_VERSION "${BROTLI_VERSION_MAJOR}.${BROTLI_VERSION_MINOR}.${BROTLI_VERSION_PATCH}")
    mark_as_advanced(BROTLI_VERSION BROTLI_VERSION_MAJOR BROTLI_VERSION_MINOR BROTLI_VERSION_PATCH)
    
    # ABI Version information
    read_macro("c/common/version.h" "BROTLI_ABI_CURRENT" BROTLI_ABI_CURRENT)
    read_macro("c/common/version.h" "BROTLI_ABI_REVISION" BROTLI_ABI_REVISION)
    read_macro("c/common/version.h" "BROTLI_ABI_AGE" BROTLI_ABI_AGE)
    math(EXPR BROTLI_ABI_COMPATIBILITY "${BROTLI_ABI_CURRENT} - ${BROTLI_ABI_AGE}")
    mark_as_advanced(BROTLI_ABI_CURRENT BROTLI_ABI_REVISION BROTLI_ABI_AGE BROTLI_ABI_COMPATIBILITY)
    
    if (ENABLE_SANITIZER)
      set(CMAKE_C_FLAGS " ${CMAKE_C_FLAGS} -fsanitize=${ENABLE_SANITIZER}")
      set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fsanitize=${ENABLE_SANITIZER}")
      set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=${ENABLE_SANITIZER}")
    endif ()
    
    include(CheckLibraryExists)
    set(LIBM_LIBRARY)
    set(LIBM_DEP)
    CHECK_LIBRARY_EXISTS(m log2 "" HAVE_LIB_M)
    if (HAVE_LIB_M)
      set(LIBM_LIBRARY "m")
      if (NOT BUILD_SHARED_LIBS)
        set(LIBM_DEP "-lm")
      endif()
    endif()
    
    set(BROTLI_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/c/include")
    mark_as_advanced(BROTLI_INCLUDE_DIRS)
    
    if (BROTLI_BUILD_FOR_PACKAGE)
      set(BROTLI_SHARED_LIBRARIES brotlienc brotlidec brotlicommon)
      set(BROTLI_STATIC_LIBRARIES brotlienc-static brotlidec-static brotlicommon-static)
      set(BROTLI_LIBRARIES ${BROTLI_SHARED_LIBRARIES} ${LIBM_LIBRARY})
    else()  # NOT BROTLI_BUILD_FOR_PACKAGE
      if (BUILD_SHARED_LIBS)
        set(BROTLI_SHARED_LIBRARIES brotlienc brotlidec brotlicommon)
        set(BROTLI_STATIC_LIBRARIES)
      else()  # NOT BUILD_SHARED_LIBS
        set(BROTLI_SHARED_LIBRARIES)
        set(BROTLI_STATIC_LIBRARIES brotlienc brotlidec brotlicommon)
      endif()
      set(BROTLI_LIBRARIES ${BROTLI_SHARED_LIBRARIES} ${BROTLI_STATIC_LIBRARIES} ${LIBM_LIBRARY})
    endif()  # BROTLI_BUILD_FOR_PACKAGE
    mark_as_advanced(BROTLI_LIBRARIES)
    
    if (MSVC)
      message(STATUS "Defining _CRT_SECURE_NO_WARNINGS to avoid warnings about security")
    
      add_definitions(-D_CRT_SECURE_NO_WARNINGS)
    endif()
    
    file(GLOB_RECURSE BROTLI_COMMON_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} c/common/*.c)
    file(GLOB_RECURSE BROTLI_DEC_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} c/dec/*.c)
    file(GLOB_RECURSE BROTLI_ENC_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} c/enc/*.c)
    
    add_library(brotlicommon ${BROTLI_COMMON_SOURCES})
    add_library(brotlidec ${BROTLI_DEC_SOURCES})
    add_library(brotlienc ${BROTLI_ENC_SOURCES})
    
    if (BROTLI_BUILD_FOR_PACKAGE)
      add_library(brotlicommon-static STATIC ${BROTLI_COMMON_SOURCES})
      add_library(brotlidec-static STATIC ${BROTLI_DEC_SOURCES})
      add_library(brotlienc-static STATIC ${BROTLI_ENC_SOURCES})
    endif()
    
    # Older CMake versions does not understand INCLUDE_DIRECTORIES property.
    include_directories(${BROTLI_INCLUDE_DIRS})
    
    if (BUILD_SHARED_LIBS)
      foreach(lib ${BROTLI_SHARED_LIBRARIES})
        target_compile_definitions(${lib} PUBLIC "BROTLI_SHARED_COMPILATION" )
        string(TOUPPER "${lib}" LIB)
        set_target_properties (${lib} PROPERTIES DEFINE_SYMBOL "${LIB}_SHARED_COMPILATION")
      endforeach()
    endif()  # BUILD_SHARED_LIBS
    
    foreach(lib ${BROTLI_SHARED_LIBRARIES} ${BROTLI_STATIC_LIBRARIES})
      target_link_libraries(${lib} ${LIBM_LIBRARY})
      set_property(TARGET ${lib} APPEND PROPERTY INCLUDE_DIRECTORIES ${BROTLI_INCLUDE_DIRS})
      # For Android, use unversioned SONAME by not setting VERSION/SOVERSION at all
      # Detect Android by checking if compiler contains "android"
      if (NOT (ANDROID OR CMAKE_SYSTEM_NAME MATCHES "Android" OR CMAKE_C_COMPILER MATCHES "android"))
        set_target_properties(${lib} PROPERTIES
          VERSION "${BROTLI_ABI_COMPATIBILITY}.${BROTLI_ABI_AGE}.${BROTLI_ABI_REVISION}"
          SOVERSION "${BROTLI_ABI_COMPATIBILITY}")
      endif()
      if (NOT BROTLI_EMSCRIPTEN)
        set_target_properties(${lib} PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
      endif()
      set_property(TARGET ${lib} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${BROTLI_INCLUDE_DIRS}>")
    endforeach()  # BROTLI_xxx_LIBRARIES
    
    target_link_libraries(brotlidec brotlicommon)
    target_link_libraries(brotlienc brotlicommon)
    
    # For projects stuck on older versions of CMake, this will set the
    # BROTLI_INCLUDE_DIRS and BROTLI_LIBRARIES variables so they still
    # have a relatively easy way to use Brotli:
    #
    #   include_directories(${BROTLI_INCLUDE_DIRS})
    #   target_link_libraries(foo ${BROTLI_LIBRARIES})
    if (BROTLI_PARENT_DIRECTORY)
      set(BROTLI_INCLUDE_DIRS "${BROTLI_INCLUDE_DIRS}" PARENT_SCOPE)
      set(BROTLI_LIBRARIES "${BROTLI_LIBRARIES}" PARENT_SCOPE)
    endif()
    
    # Build the brotli executable
    if (BROTLI_BUILD_TOOLS)
      add_executable(brotli c/tools/brotli.c)
      target_link_libraries(brotli ${BROTLI_LIBRARIES})
    endif()
    
    # Installation
    if (NOT BROTLI_BUNDLED_MODE)
      if (BROTLI_BUILD_TOOLS)
        install(
          TARGETS brotli
          RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
        )
      endif()
    
      install(
        TARGETS ${BROTLI_SHARED_LIBRARIES} ${BROTLI_STATIC_LIBRARIES}
        ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
        LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
        RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
      )
    
      install(
        DIRECTORY ${BROTLI_INCLUDE_DIRS}/brotli
        DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
      )
    endif()  # BROTLI_BUNDLED_MODE
    
    # Tests
    
    # Integration tests, those depend on `brotli` binary
    if (NOT BROTLI_DISABLE_TESTS AND BROTLI_BUILD_TOOLS)
      # If we're targeting Windows but not running on Windows, we need Wine
      # to run the tests...
      if (WIN32 AND NOT CMAKE_HOST_WIN32)
        find_program(BROTLI_WRAPPER NAMES wine)
    
        if (NOT BROTLI_WRAPPER)
          message(STATUS "wine not found, disabling tests")
          set(BROTLI_DISABLE_TESTS TRUE)
        endif()
      endif()  # WIN32 emulation
      if (BROTLI_EMSCRIPTEN)
        find_program(BROTLI_WRAPPER NAMES node)
        if (NOT BROTLI_WRAPPER)
          message(STATUS "node not found, disabling tests")
          set(BROTLI_DISABLE_TESTS TRUE)
        endif()
      endif()  # BROTLI_EMSCRIPTEN
    endif()  # BROTLI_DISABLE_TESTS
    
    # NB: BROTLI_DISABLE_TESTS might have changed.
    if (NOT BROTLI_DISABLE_TESTS AND BROTLI_BUILD_TOOLS)
      # If our compiler is a cross-compiler that we know about (arm/aarch64),
      # then we need to use qemu to execute the tests.
      if ("${CMAKE_C_COMPILER}" MATCHES "^.*/arm-linux-gnueabihf-.*$")
        message(STATUS "Detected arm-linux-gnueabihf cross-compilation")
        set(BROTLI_WRAPPER "qemu-arm")
        set(BROTLI_WRAPPER_LD_PREFIX "/usr/arm-linux-gnueabihf")
      endif()
    
      if ("${CMAKE_C_COMPILER}" MATCHES "^.*/arm-linux-gnueabi-.*$")
        message(STATUS "Detected arm-linux-gnueabi cross-compilation")
        set(BROTLI_WRAPPER "qemu-arm")
        set(BROTLI_WRAPPER_LD_PREFIX "/usr/arm-linux-gnueabi")
      endif()
    
      if ("${CMAKE_C_COMPILER}" MATCHES "^.*/aarch64-linux-gnu-.*$")
        message(STATUS "Detected aarch64-linux-gnu cross-compilation")
        set(BROTLI_WRAPPER "qemu-aarch64")
        set(BROTLI_WRAPPER_LD_PREFIX "/usr/aarch64-linux-gnu")
      endif()
    
      include(CTest)
      enable_testing()
    
      set(ROUNDTRIP_INPUTS
        tests/testdata/alice29.txt
        tests/testdata/asyoulik.txt
        tests/testdata/lcet10.txt
        tests/testdata/plrabn12.txt
        c/enc/encode.c
        c/common/dictionary.h
        c/dec/decode.c)
    
      foreach(INPUT ${ROUNDTRIP_INPUTS})
        get_filename_component(OUTPUT_NAME "${INPUT}" NAME)
    
        set(OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_NAME}")
        set(INPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}")
    
        if (EXISTS "${INPUT_FILE}")
          foreach(quality 1 6 9 11)
            add_test(NAME "${BROTLI_TEST_PREFIX}roundtrip/${INPUT}/${quality}"
              COMMAND "${CMAKE_COMMAND}"
                -DBROTLI_WRAPPER=${BROTLI_WRAPPER}
                -DBROTLI_WRAPPER_LD_PREFIX=${BROTLI_WRAPPER_LD_PREFIX}
                -DBROTLI_CLI=$<TARGET_FILE:brotli>
                -DQUALITY=${quality}
                -DINPUT=${INPUT_FILE}
                -DOUTPUT=${OUTPUT_FILE}.${quality}
                -P ${CMAKE_CURRENT_SOURCE_DIR}/tests/run-roundtrip-test.cmake)
          endforeach()
        else()
          message(NOTICE "Test file ${INPUT} does not exist; OK on tarball builds; consider running scripts/download_testdata.sh before configuring.")
        endif()
      endforeach()
    
      file(GLOB_RECURSE
        COMPATIBILITY_INPUTS
        RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
        tests/testdata/*.compressed*)
    
      foreach(INPUT ${COMPATIBILITY_INPUTS})
        string(REGEX REPLACE "([a-zA-Z0-9\\.]+)\\.compressed(\\.[0-9]+)?$" "\\1" UNCOMPRESSED_INPUT "${INPUT}")
        if (EXISTS ${UNCOMPRESSED_INPUT})
          add_test(NAME "${BROTLI_TEST_PREFIX}compatibility/${INPUT}"
            COMMAND "${CMAKE_COMMAND}"
              -DBROTLI_WRAPPER=${BROTLI_WRAPPER}
              -DBROTLI_WRAPPER_LD_PREFIX=${BROTLI_WRAPPER_LD_PREFIX}
              -DBROTLI_CLI=$<TARGET_FILE:brotli>
              -DINPUT=${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}
              -P ${CMAKE_CURRENT_SOURCE_DIR}/tests/run-compatibility-test.cmake)
        endif()
      endforeach()
    endif()  # BROTLI_DISABLE_TESTS
    
    # Generate a pkg-config files
    
    function(generate_pkg_config_path outvar path)
      string(LENGTH "${path}" path_length)
    
      set(path_args ${ARGV})
      list(REMOVE_AT path_args 0 1)
      list(LENGTH path_args path_args_remaining)
    
      set("${outvar}" "${path}")
    
      while(path_args_remaining GREATER 1)
        list(GET path_args 0 name)
        list(GET path_args 1 value)
    
        get_filename_component(value_full "${value}" ABSOLUTE)
        string(LENGTH "${value}" value_length)
    
        if (path_length EQUAL value_length AND path STREQUAL value)
          set("${outvar}" "\${${name}}")
          break()
        elseif (path_length GREATER value_length)
          # We might be in a subdirectory of the value, but we have to be
          # careful about a prefix matching but not being a subdirectory
          # (for example, /usr/lib64 is not a subdirectory of /usr/lib).
          # We'll do this by making sure the next character is a directory
          # separator.
          string(SUBSTRING "${path}" ${value_length} 1 sep)
          if (sep STREQUAL "/")
            string(SUBSTRING "${path}" 0 ${value_length} s)
            if (s STREQUAL value)
              string(SUBSTRING "${path}" "${value_length}" -1 suffix)
              set("${outvar}" "\${${name}}${suffix}")
              break()
            endif()
          endif()
        endif()
    
        list(REMOVE_AT path_args 0 1)
        list(LENGTH path_args path_args_remaining)
      endwhile()
    
      set("${outvar}" "${${outvar}}" PARENT_SCOPE)
    endfunction(generate_pkg_config_path)
    
    function(transform_pc_file INPUT_FILE OUTPUT_FILE VERSION)
      file(READ ${INPUT_FILE} TEXT)
    
      set(PREFIX "${CMAKE_INSTALL_PREFIX}")
      string(REGEX REPLACE "@prefix@" "${PREFIX}" TEXT ${TEXT})
      string(REGEX REPLACE "@exec_prefix@" "${PREFIX}" TEXT ${TEXT})
      string(REGEX REPLACE "@libm@" "${LIBM_DEP}" TEXT ${TEXT})
    
      generate_pkg_config_path(LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}" prefix "${PREFIX}")
      string(REGEX REPLACE "@libdir@" "${LIBDIR}" TEXT ${TEXT})
    
      generate_pkg_config_path(INCLUDEDIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}" prefix "${PREFIX}")
      string(REGEX REPLACE "@includedir@" "${INCLUDEDIR}" TEXT ${TEXT})
    
      string(REGEX REPLACE "@PACKAGE_VERSION@" "${VERSION}" TEXT ${TEXT})
    
      file(WRITE ${OUTPUT_FILE} ${TEXT})
    endfunction()
    
    transform_pc_file("scripts/libbrotlicommon.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libbrotlicommon.pc" "${BROTLI_VERSION}")
    
    transform_pc_file("scripts/libbrotlidec.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libbrotlidec.pc" "${BROTLI_VERSION}")
    
    transform_pc_file("scripts/libbrotlienc.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libbrotlienc.pc" "${BROTLI_VERSION}")
    
    if (NOT BROTLI_BUNDLED_MODE)
      install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbrotlicommon.pc"
        DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
      install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbrotlidec.pc"
        DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
      install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbrotlienc.pc"
        DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
    endif()  # BROTLI_BUNDLED_MODE
    
    if (BROTLI_BUILD_TOOLS)
      install(FILES "docs/brotli.1"
        DESTINATION "${CMAKE_INSTALL_FULL_MANDIR}/man1")
    endif()
    
    install(FILES docs/constants.h.3 docs/decode.h.3 docs/encode.h.3 docs/types.h.3
      DESTINATION "${CMAKE_INSTALL_FULL_MANDIR}/man3")
    
    if (ENABLE_COVERAGE STREQUAL "yes")
      setup_target_for_coverage(coverage test coverage)
    endif()