# Copyright (C) 2019-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.

if(WIN32)
    cmake_minimum_required( VERSION 3.16 ) # For file(GET_RUNTIME_DEPENDENCIES)
else()
    cmake_minimum_required( VERSION 3.14 )
endif()

set(CMAKE_C_STANDARD 90)
set(CMAKE_C_STANDARD_REQUIRED ON)
# CMAKE_CXX_STANDARD not set because we may need to match the compiler used to build LLVM.
# There's probably a good chance this issue will go away if using modern LLVM versions.
cmake_policy(SET CMP0087 NEW) # support generator expressions in install(CODE) and install(SCRIPT)

# Change this on a release:
#  During active development: set(VERSION_SUFFIX "-devel-${TODAY}")
#  For beta:                  set(VERSION_SUFFIX "-beta")
#  For release candidate:     set(VERSION_SUFFIX "-rc")
#  For release:               set(VERSION_SUFFIX "")
string(TIMESTAMP TODAY "%Y%m%d")
set(VERSION_SUFFIX "")

project( ClamAV
         VERSION "1.0.5"
         DESCRIPTION "ClamAV open source email, web, and end-point anti-virus toolkit." )

set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
include(Version)

set(PACKAGE_NAME      "${PROJECT_NAME}")
set(PACKAGE_VERSION   "${PROJECT_VERSION}")
set(PACKAGE_STRING    "${PROJECT_NAME} ${PROJECT_VERSION}${VERSION_SUFFIX}")
set(PACKAGE_BUGREPORT "https://github.com/Cisco-Talos/clamav/issues")
set(PACKAGE_URL       "https://www.clamav.net/")
HexVersion(PACKAGE_VERSION_NUM ${PROJECT_VERSION_MAJOR} ${PROJECT_VERSION_MINOR} ${PROJECT_VERSION_PATCH})

# libtool library versioning rules: http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
set(LIBCLAMAV_CURRENT  11)
set(LIBCLAMAV_REVISION 0)
set(LIBCLAMAV_AGE      0)

math(EXPR LIBCLAMAV_SOVERSION "${LIBCLAMAV_CURRENT} - ${LIBCLAMAV_AGE}")
set(LIBCLAMAV_VERSION "${LIBCLAMAV_SOVERSION}.${LIBCLAMAV_AGE}.${LIBCLAMAV_REVISION}")
HexVersion(LIBCLAMAV_VERSION_NUM ${LIBCLAMAV_CURRENT} ${LIBCLAMAV_REVISION} ${LIBCLAMAV_AGE})

set(LIBFRESHCLAM_CURRENT  2)
set(LIBFRESHCLAM_REVISION 2)
set(LIBFRESHCLAM_AGE      0)

math(EXPR LIBFRESHCLAM_SOVERSION "${LIBFRESHCLAM_CURRENT} - ${LIBFRESHCLAM_AGE}")
set(LIBFRESHCLAM_VERSION "${LIBFRESHCLAM_SOVERSION}.${LIBFRESHCLAM_AGE}.${LIBFRESHCLAM_REVISION}")
HexVersion(LIBFRESHCLAM_VERSION_NUM ${LIBFRESHCLAM_CURRENT} ${LIBFRESHCLAM_REVISION} ${LIBFRESHCLAM_AGE})

set(GENERATE_WARNING "WARNING: This file was generated by CMake. Do not edit!")

#
# Define C_LINUX and C_BSD because CMake only defines UNIX, APPLE, WIN32, MSVC
#
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
    set(C_LINUX 1)
elseif(APPLE OR CMAKE_SYSTEM_NAME MATCHES "BSD")
    set(C_BSD 1)
endif()

# Git optionally used to add commit info into build to differentiate in bug reports.
find_package(Git)
if(Git_FOUND)
    # Store git description into variable
    execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --always
        OUTPUT_VARIABLE REPO_VERSION ERROR_QUIET)
    if("${REPO_VERSION}" MATCHES "")
        unset(REPO_VERSION)
    else()
        string(STRIP ${REPO_VERSION} REPO_VERSION)
    endif()
endif()

# Enable use of pkg-config to find depenencies.
find_package(PkgConfig QUIET)

#
# Load Build Options
#

# CMake Option default values:
set(MAINTAINER_MODE_DEFAULT         OFF)
set(ENABLE_APP_DEFAULT              ON)
if(WIN32 OR APPLE)
    set(ENABLE_MILTER_DEFAULT       OFF)
else()
    set(ENABLE_MILTER_DEFAULT       ON)
endif()
if(C_LINUX)
    set(ENABLE_CLAMONACC_DEFAULT    ON)
else()
    set(ENABLE_CLAMONACC_DEFAULT    OFF)
endif()
set(ENABLE_EXAMPLES_DEFAULT         OFF)
set(ENABLE_TESTS_DEFAULT            ON)
if(WIN32)
    set(ENABLE_MAN_PAGES_DEFAULT    OFF)
else()
    set(ENABLE_MAN_PAGES_DEFAULT    ON)
endif()
set(ENABLE_DOXYGEN_DEFAULT          OFF)
set(ENABLE_UNRAR_DEFAULT            ON)
set(ENABLE_SYSTEMD_DEFAULT          ON)

# See CMakeOptions.cmake for additional options.
include(CMakeOptions.cmake)

# Set build type to RelWithDebInfo if not specified.
# This must be done before pulling in the Rust module
# or else the default build will be Debug for Rust stuffs.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the build type" FORCE)

    # Include "None" as option to disable any additional (optimization) flags,
    # relying on just CMAKE_C_FLAGS and CMAKE_CXX_FLAGS (which are empty by
    # default). These strings are presented in cmake-gui.
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
        "None" "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()

#
# Find Build Tools
#
if(MAINTAINER_MODE)
    # Bison, Flex required to build Yara module for libclamav.
    find_package(BISON REQUIRED)
    find_package(FLEX  REQUIRED)

    # TODO: Gperf required to generate JS-normalization code.
    # find_package(GPERF REQUIRED)
endif()
find_package(Rust REQUIRED)

if(ENABLE_FUZZ)
    # We'd like the fuzz targets to be statically linked
    set(ENABLE_STATIC_LIB       ON)
    set(ENABLE_SHARED_LIB       OFF)
    set(ENABLE_LIBCLAMAV_ONLY   ON)
    set(ENABLE_TESTS            OFF)
    set(OPTIMIZE                OFF)
    set(ENABLE_SYSTEMD          OFF)
    set(HAVE_MMAP               OFF)

    if(NOT DEFINED ENV{FUZZING_ENGINE})
        # If you're not building in an oss-fuzz docker environment, you can build the fuzz targets by setting:
        # bash:  export CC=`which clang` && export CXX=`which clang++` && export SANITIZER=address/undefined/memory
        # fish:  export CC=(which clang) && export CXX=(which clang++) && export SANITIZER=address/undefined/memory
        set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS} -fsanitize=$ENV{SANITIZER},fuzzer-no-link -fsanitize-coverage=edge,indirect-calls")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=$ENV{SANITIZER},fuzzer-no-link -fsanitize-coverage=edge,indirect-calls")
    endif()
endif()

if(ENABLE_STATIC_LIB AND NOT ENABLE_SHARED_LIB AND ENABLE_UNRAR)
    # When building everything static, link in libunrar.
    set(UNRAR_LINKED 1)
endif()

if(ENABLE_LIBCLAMAV_ONLY AND (ENABLE_APP OR ENABLE_EXAMPLES))
    # Remember when disabled options are disabled for later diagnostics.
    set(ENABLE_LIB_ONLY_DISABLED_OTHERS 1)
else()
    set(ENABLE_LIB_ONLY_DISABLED_OTHERS 0)
endif()
if(ENABLE_LIBCLAMAV_ONLY)
    set(ENABLE_APP                  OFF)
    set(ENABLE_EXAMPLES             OFF)
endif()

#
# Set RPATH for custom install prefixes
#
if(APPLE)
    set(CMAKE_MACOSX_RPATH 1)
endif()

include(GNUInstallDirs)

if (NOT DEFINED CMAKE_INSTALL_RPATH)
    if(CMAKE_INSTALL_FULL_LIBDIR)
        set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}")
    else()
        set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
    endif()
endif()

if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
  set(USING_CLANG ON)
else()
  set(USING_CLANG OFF)
endif()

if(C_LINUX)
    if(CMAKE_COMPILER_IS_GNUCXX OR USING_CLANG)
        # Set _GNU_SOURCE for O_LARGEFILE, O_CLOEXEC, O_DIRECTORY, O_NOFOLLOW, etc flags on older systems
        # (pre POSIX.1-2008: glibc 2.11 and earlier). #4042
        set(_GNU_SOURCE 1)
    endif()
endif()

#
# Use the `lib` prefix on Windows, to match previous ClamAV build
#
if(LLVM_FOUND)
    list(JOIN LLVM_LIBRARIES "," LLVM_LIBS)
    list(JOIN LLVM_LIBRARY_DIRS "," LLVM_DIRS)
endif()

if(WIN32)
    set(CMAKE_SHARED_LIBRARY_PREFIX "lib")
    set(CMAKE_STATIC_LIBRARY_PREFIX "lib")
endif()

#
# Find Test dependencies
#
if(ENABLE_TESTS)
    # Used for the libclamav unit test framework
    find_package(Libcheck REQUIRED)

    # Used to generate the test files and for the application feature test framework
    find_package(Python3 REQUIRED)

    # First try using pytest from the PATH
    execute_process(
        COMMAND pytest --version
        RESULT_VARIABLE PYTEST_EXIT_CODE
        ERROR_QUIET OUTPUT_QUIET
    )

    if(${PYTEST_EXIT_CODE} EQUAL 0)
        # pytest found in the path.
        set(PythonTest_COMMAND "pytest;-v")
    else()
        # Not in the path, try using: python3 -m pytest
        execute_process(
            COMMAND ${Python3_EXECUTABLE} -m pytest --version
            RESULT_VARIABLE PYTEST_MODULE_EXIT_CODE
            ERROR_QUIET OUTPUT_QUIET
        )

        if(${PYTEST_MODULE_EXIT_CODE} EQUAL 0)
            # pytest isn't in the path, but the Python 3 we found has it.
            set(PythonTest_COMMAND "${Python3_EXECUTABLE};-m;pytest;-v")
        else()
            # pytest couldn't be found, verify that we can at least use: python3 -m unittest
            execute_process(
                COMMAND ${Python3_EXECUTABLE} -m unittest --help
                RESULT_VARIABLE UNITTEST_MODULE_EXIT_CODE
                ERROR_QUIET OUTPUT_QUIET
            )

            if(${UNITTEST_MODULE_EXIT_CODE} EQUAL 0)
                # No pytest :-(, but we'll get by with unittest
                message("Python 3 package 'pytest' is not installed for ${Python3_EXECUTABLE} and is not available in your PATH.")
                message("Failed unit tests will be easier to read if you install pytest.")
                message("Eg:  python3 -m pip install --user pytest")

                set(PythonTest_COMMAND "${Python3_EXECUTABLE};-m;unittest;--verbose")
            else()
                # No unittest either!
                # Some weird Python installations do exist that lack standard modules like unittest.
                # Let's make sure these folks know the Python 3 install we found won't cut it.
                message("Python 3 found: ${Python3_EXECUTABLE}, but it is missing the unittest module (wierd!).")
                message(FATAL_ERROR "The tests won't work with this Python installation. You can disable the tests by reconfiguring with: -D ENABLE_TESTS=OFF")
            endif()
        endif()
    endif()

    # Check for valgrind. If it exists, we'll enable extra tests that use valgrind.
    if(C_LINUX)
        find_package(Valgrind)
    endif()
endif()

#
# Configure CPack
#  - for installers
#  - for source tarball
#

if (WIN32)
    find_package(PThreadW32)
    set(HAVE_PTHREAD_H 1)
    set(_REENTRANT 1)
    set(CL_THREAD_SAFE 1)
else()
    set(CMAKE_THREAD_PREFER_PTHREAD 1)
    find_package(Threads)
    if(Threads_FOUND AND CMAKE_USE_PTHREADS_INIT)
        set(HAVE_PTHREAD_H 1)
        set(_REENTRANT 1)
        set(CL_THREAD_SAFE 1)
    endif()
endif()

#
# Configure CPack
#  - for installers
#  - for source tarball
#

set(CPACK_PACKAGE_NAME ${PACKAGE_STRING})

#set(CPACK_PACKAGE_ICON ${PROJECT_SOURCE_DIR}/logo.png)
#set(CPACK_PACKAGE_RELOCATABLE TRUE)
set(CPACK_STRIP_FILES TRUE)

set(CPACK_PACKAGE_VERSION_MAJOR ${CLAMAV_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${CLAMAV_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${CLAMAV_VERSION_PATCH})
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_DESCRIPTION})
set(CPACK_PACKAGE_CONTACT https://www.clamav.net/)

# point to readme and license files
set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.md)
set(CPACK_RESOURCE_FILE_LICENSE ${PROJECT_SOURCE_DIR}/COPYING.txt)

set(CPACK_PACKAGE_FILE_NAME clamav-${PROJECT_VERSION}${VERSION_SUFFIX})

set(CPACK_SOURCE_GENERATOR "TGZ")
set(CPACK_SOURCE_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME})
set(CPACK_SOURCE_IGNORE_FILES "\\.git/" ".*~$" "^${CMAKE_BINARY_DIR}")
set(CPACK_VERBATIM_VARIABLES YES)

if (WIN32)
    set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION .)
    include(InstallRequiredSystemLibraries)

    set(CPACK_GENERATOR                  "ZIP;WIX")
    set(CPACK_PACKAGE_VENDOR             "Cisco Systems, Inc.")
    set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME}.win.${CMAKE_VS_PLATFORM_NAME})

    NumberToHex(${PROJECT_VERSION_MAJOR} HEX_MAJOR)
    NumberToHex(${PROJECT_VERSION_MINOR} HEX_MINOR)
    NumberToHex(${PROJECT_VERSION_PATCH} HEX_PATCH)
    SET(CPACK_WIX_PRODUCT_GUID           "D9F136C1-3691-47E3-9079-4FE9C9${HEX_MAJOR}${HEX_MINOR}${HEX_PATCH}")
    SET(CPACK_WIX_UPGRADE_GUID           "D9F136C1-3691-47E3-9079-4FE9C9${HEX_MAJOR}${HEX_MINOR}00")

    SET(CPACK_WIX_PRODUCT_ICON           ${CMAKE_CURRENT_SOURCE_DIR}/win32/res/clam.ico)
    SET(CPACK_WIX_UI_BANNER              ${CMAKE_CURRENT_SOURCE_DIR}/win32/wix_ui_banner.bmp)
    SET(CPACK_WIX_UI_DIALOG              ${CMAKE_CURRENT_SOURCE_DIR}/win32/wix_ui_dialog.bmp)
    SET(CPACK_WIX_CMAKE_PACKAGE_REGISTRY ClamAV)
    set(CPACK_PACKAGE_INSTALL_DIRECTORY  "ClamAV")

    install(
        FILES
            ${CMAKE_CURRENT_SOURCE_DIR}/NEWS.md
            ${CMAKE_CURRENT_SOURCE_DIR}/README.md
        DESTINATION ".")

elseif(APPLE)
    set(CPACK_PACKAGE_NAME      ClamAV)
    set(CPACK_GENERATOR         productbuild)
    set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME}.macos.universal)
    set(CPACK_PACKAGE_VENDOR    "cisco")  # For Apple, creates short name com.cisco.clamav.xxx in pkgutil

    # productbuild requires components or it won't add anything to the PKG
    set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries")
    set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION "The ClamAV libraries including the header files for C/C++ development.")
    set(CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "Documentation")
    set(CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION "Manpages and HTML documentation.")
    set(CPACK_COMPONENT_PROGRAMS_DISPLAY_NAME "Programs")
    set(CPACK_COMPONENT_PROGRAMS_DESCRIPTION "The ClamAV toolkit.")

    # dependencies between components
    set(CPACK_COMPONENT_DOCUMENTATION_DEPENDS libraries)
    set(CPACK_COMPONENT_PROGRAMS_DEPENDS libraries)
    set(CPACK_COMPONENT_LIBRARIES_REQUIRED ON) # always install the libs
    set(CPACK_COMPONENTS_ALL libraries documentation programs)

    if(CLAMAV_SIGN_FILE)
        # This tells Xcode's signing phase to pretend that the binary was signed by the linker.
        # Then install_name_tool is willing to revise the ad-hoc signature.
        # See: https://gitlab.kitware.com/cmake/cmake/-/issues/21854#note_907691
        set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "-o linker-signed")

        # Stripping the files will invalidate the signatures.
        set(CPACK_STRIP_FILES FALSE)

        # Convert NEWS.md to HTML to match Apple productbuild requirements.
        # Not in the path, try using: python3 -m pytest
        execute_process(
            COMMAND ${Python3_EXECUTABLE} -m markdown --version
            RESULT_VARIABLE MARKDOWN_MODULE_EXIT_CODE
            ERROR_QUIET OUTPUT_QUIET
        )
        if(${MARKDOWN_MODULE_EXIT_CODE} EQUAL 0)
            # The markdown module is installed, we can do the conversion.
            execute_process(
                COMMAND echo "<html><body>"
                OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/prefix.html)
            execute_process(
                COMMAND ${Python3_EXECUTABLE} -m markdown ${CMAKE_CURRENT_SOURCE_DIR}/NEWS.md
                OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/body.html)
            execute_process(
                COMMAND echo "</html></body>"
                OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/suffix.html)
            execute_process(
                COMMAND cat ${CMAKE_CURRENT_BINARY_DIR}/prefix.html
                            ${CMAKE_CURRENT_BINARY_DIR}/body.html
                            ${CMAKE_CURRENT_BINARY_DIR}/suffix.html
                OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/NEWS.html)

            set(CPACK_RESOURCE_FILE_README ${PROJECT_BINARY_DIR}/NEWS.html)
        else()
            message("Failed to detect the Python 3 'markdown' package.")
            message(FATAL_ERROR "Try running 'pip3 install --user markdown' and then try again.")
        endif()
    endif()

elseif(C_BSD)
    # User must specify `-G FreeBSD` when building FreeBSD packages
    # Disclaimer: doesn't seem to actually exist, despite being in the documentation.
    set(CPACK_PACKAGE_NAME             clamav)
    set(CPACK_PACKAGE_FILE_NAME        ${CPACK_PACKAGE_FILE_NAME}.freebsd.${CMAKE_SYSTEM_PROCESSOR})

    set(CPACK_FREEBSD_PACKAGE_LICENSE "GPLv2")
    set(CPACK_FREEBSD_PACKAGE_MAINTAINER "https://www.clamav.net/")

else()
    # User must specify `-G DEB` or `-G RPM` when building Linux packages
    set(CPACK_PACKAGE_NAME             clamav)
    set(CPACK_PACKAGE_VERSION          ${PROJECT_VERSION}${VERSION_SUFFIX})
    set(CPACK_PACKAGE_FILE_NAME        ${CPACK_PACKAGE_FILE_NAME}.linux.${CMAKE_SYSTEM_PROCESSOR})

    set(CPACK_RPM_PACKAGE_LICENSE "GPLv2")
    set(CPACK_RPM_PACKAGE_AUTOREQ " no")
    set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/usr/sbin"
        "/usr/local"
        "/usr/local/bin"
        "/usr/local/etc"
        "/usr/local/include"
        "/usr/local/lib"
        "/usr/local/sbin"
        "/usr/local/share"
        "/usr/local/share/man"
        "/usr/local/share/man/man1"
        "/usr/local/share/man/man5"
        "/usr/local/share/man/man8"
    )
endif()

# Including CPack must be the very last CPack thing we do.
include(CPack)

# libclamav efficacy dependencies
find_package(OpenSSL REQUIRED)
if(OPENSSL_FOUND)
    set(HAVE_LIBSSL 1)
endif()

find_package(ZLIB REQUIRED)
if(ZLIB_FOUND)
    set(HAVE_ZLIB_H 1)
    set(HAVE_LIBZ 1)
endif()

find_package(BZip2 REQUIRED)
if(BZIP2_FOUND)
    set(HAVE_BZLIB_H 1)
endif()

# Disable CMAKE_FIND_PACKAGE_PREFER_CONFIG, temporarily, because
# we don't presently support the using libxml2's Config.cmake
set(PACKAGE_PREFER_CONFIG_BAK ${CMAKE_FIND_PACKAGE_PREFER_CONFIG})
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG FALSE)

find_package(LibXml2 REQUIRED)
if(LibXml2_FOUND)
    set(HAVE_LIBXML2 1)
endif()
# Restore the requested CMAKE_FIND_PACKAGE_PREFER_CONFIG setting
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ${PACKAGE_PREFER_CONFIG_BAK})

find_package(PCRE2 REQUIRED)
if(PCRE2_FOUND)
    set(HAVE_PCRE 1)
    set(USING_PCRE2 1)
endif()

# libclamav feature dependencies
if(NOT WIN32)
    find_package(Iconv REQUIRED)
    # Set variable required by libclamav to use iconv
    set(HAVE_ICONV 1)
endif()

if(ENABLE_JSON_SHARED)
    set(JSONC_USE_STATIC OFF)
else()
    set(JSONC_USE_STATIC ON)
endif()
find_package(JSONC REQUIRED)
# Set variable required by libclamav to use libjson-c
set(HAVE_JSON 1)

set(LLVM_MAX_VER "13")
set(LLVM_MIN_VER "8")

string (TOLOWER ${BYTECODE_RUNTIME} bytecodeRuntime)
if(${bytecodeRuntime} STREQUAL "llvm")
    if(DEFINED LLVM_ROOT_DIR AND DEFINED LLVM_FIND_VERSION)
        find_package(LLVM EXACT ${LLVM_FIND_VERSION} REQUIRED HINTS ${LLVM_ROOT_DIR})
    elseif(DEFINED LLVM_ROOT_DIR)
        find_package(LLVM REQUIRED HINTS ${LLVM_ROOT_DIR})
    elseif(DEFINED LLVM_FIND_VERSION)
        find_package(LLVM EXACT ${LLVM_FIND_VERSION} REQUIRED)
    else()
        set (LLVM_FIND_VERSION ${LLVM_MIN_VER})
        find_package(LLVM REQUIRED)
    endif()
    if(LLVM_FOUND)
        if (LLVM_AVAILABLE_LIBS)
            message(STATUS "LLVM found using LLVMConfig.cmake")
            set(LLVM_LIBRARIES ${LLVM_AVAILABLE_LIBS})
        else()
            message(STATUS "LLVM found using FindLLVM.cmake")
            set(LLVM_PACKAGE_VERSION ${LLVM_VERSION_STRING})

            if (${LLVM_VERSION_STRING} VERSION_GREATER_EQUAL "9.0.0" AND ${LLVM_VERSION_STRING} VERSION_LESS "10.0.0")
                set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG")
            endif()
        endif()

        math(EXPR TOO_HIGH_VERSION "${LLVM_MAX_VER} + 1" OUTPUT_FORMAT DECIMAL)

        if(${LLVM_PACKAGE_VERSION} VERSION_LESS ${LLVM_MIN_VER})
            message(FATAL_ERROR "LLVM version ${LLVM_PACKAGE_VERSION} is too old")
        elseif (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL ${TOO_HIGH_VERSION} )
            message(FATAL_ERROR "LLVM version ${LLVM_PACKAGE_VERSION} is too new")
        else()
            message(STATUS "LLVM version ${LLVM_PACKAGE_VERSION} found")
        endif()

        # Set variable required by libclamav to use llvm instead of interpreter
        set(LLVM_VERSION ${LLVM_VERSION_MAJOR}${LLVM_VERSION_MINOR})
    endif()
endif()

# libfreshclam & application dependencies
if(NOT ENABLE_LIBCLAMAV_ONLY)
    find_package(CURL REQUIRED)

    if(APPLE)
        find_library(APPLE_CORE_FOUNDATION CoreFoundation)
        if (NOT APPLE_CORE_FOUNDATION)
            message(FATAL_ERROR "Apple CoreFoundation framework not found")
        endif()
        find_library(APPLE_SECURITY Security)
        if (NOT APPLE_SECURITY)
            message(FATAL_ERROR "Apple Security framework not found")
        endif()
    endif()

    if(ENABLE_APP)
        find_package(CURSES REQUIRED)

        if(NOT WIN32 AND ENABLE_MILTER)
            find_package(Milter REQUIRED)
        endif()

        if(C_LINUX AND ENABLE_SYSTEMD)
            find_package(SYSTEMD)
            if(SYSTEMD_FOUND)
                set(HAVE_SYSTEMD 1)
            endif()
        endif()

    endif()
endif()


# Do not disable assertions based on CMAKE_BUILD_TYPE.
foreach(_build_type "Release" "MinSizeRel" "RelWithDebInfo")
    foreach(_lang C CXX)
        string(TOUPPER "CMAKE_${_lang}_FLAGS_${_build_type}" _var)
        string(REGEX REPLACE "(^|)[/-]D *NDEBUG($|)" " " ${_var} "${${_var}}")
    endforeach()
endforeach()

# Disable optimizations if OPTIMIZE=OFF
if(NOT OPTIMIZE)
    # Get rid of any previous optimization flag settings...
    string(REGEX REPLACE "(\-O[011123456789])" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
    string(REGEX REPLACE "(\-O[011123456789])" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")

    # ...And substitute our own.
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0")
endif()

# Support the latest c++ standard available.
include(ExtractValidFlags)
foreach(_cxx1x_flag -std=c++14 -std=c++11)
    extract_valid_cxx_flags(_cxx1x_flag_supported ${_cxx1x_flag})
    if(_cxx1x_flag_supported)
        set(CXX1XCXXFLAGS ${_cxx1x_flag})
        break()
    endif()
endforeach()

# Always use '-fPIC'/'-fPIE' option.
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

# Checks for header files.
include(CheckIncludeFile)
check_include_file("arpa/inet.h"        HAVE_ARPA_INET_H)
check_include_file("fcntl.h"            HAVE_FCNTL_H)
check_include_file("grp.h"              HAVE_GRP_H)
check_include_file("limits.h"           HAVE_LIMITS_H)
check_include_file("malloc.h"           HAVE_MALLOC_H)
check_include_file("netdb.h"            HAVE_NETDB_H)
check_include_file("netinet/in.h"       HAVE_NETINET_IN_H)
check_include_file("poll.h"             HAVE_POLL_H)
check_include_file("pwd.h"              HAVE_PWD_H)
check_include_file("stdbool.h"          HAVE_STDBOOL_H)
check_include_file("stdlib.h"           HAVE_STDLIB_H)
check_include_file("string.h"           HAVE_STRING_H)
check_include_file("strings.h"          HAVE_STRINGS_H)
check_include_file("sys/cdefs.h"        HAVE_SYS_CDEFS_H)
check_include_file("sys/dl.h"           HAVE_SYS_DL_H)
check_include_file("sys/fileio.h"       HAVE_SYS_FILIO_H)
check_include_file("sys/mman.h"         HAVE_SYS_MMAN_H)
check_include_file("sys/param.h"        HAVE_SYS_PARAM_H)
check_include_file("sys/queue.h"        HAVE_SYS_QUEUE_H)
check_include_file("sys/select.h"       HAVE_SYS_SELECT_H)
check_include_file("sys/socket.h"       HAVE_SYS_SOCKET_H)
check_include_file("sys/stat.h"         HAVE_SYS_STAT_H)
check_include_file("sys/time.h"         HAVE_SYS_TIME_H)
check_include_file("sys/times.h"        HAVE_SYS_TIMES_H)
check_include_file("sys/uio.h"          HAVE_SYS_UIO_H)
check_include_file("syslog.h"           USE_SYSLOG)
check_include_file("termios.h"          HAVE_TERMIOS_H)
check_include_file("time.h"             HAVE_TIME_H)
check_include_file("unistd.h"           HAVE_UNISTD_H)
check_include_file("sys/fanotify.h"     HAVE_SYS_FANOTIFY_H)

if(WIN32)
    set(HAVE_RESOLV_H 1)
    set(HAVE_DIRENT_H 1)
    set(HAVE_DLFCN_H 1)
else()
    if(APPLE)
        set(BIND_8_COMPAT 1)
    endif()
    check_include_file("resolv.h"       HAVE_RESOLV_H)
    if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD AND EXISTS /usr/include/resolv.h)
        # This hack because check_include_file() can't find "resolv.h" on FreeBSD.
        set(HAVE_RESOLV_H 1)
    endif()
    check_include_file("dirent.h"       HAVE_DIRENT_H)
    check_include_file("dlfcn.h"        HAVE_DLFCN_H)
endif()

# int-types variants
check_include_file("inttypes.h"         HAVE_INTTYPES_H)
check_include_file("sys/inttypes.h"     HAVE_SYS_INTTYPES_H)
check_include_file("sys/int_types.h"    HAVE_SYS_INT_TYPES_H)
check_include_file("stdint.h"           HAVE_STDINT_H)

# this hack required to silence warnings on systems with inttypes.h
# because json-c's inttypes header isn't generated for each system. Hooray C!
if(HAVE_INTTYPES_H)
    set(JSON_C_HAVE_INTTYPES_H 1)
endif()

include(CheckTypeSize)
# Checks for typedefs, structures, and compiler characteristics.
# AC_TYPE_SIZE_T
check_type_size("ssize_t" SIZEOF_SSIZE_T)
if(SIZEOF_SSIZE_T STREQUAL "")
    # ssize_t is a signed type in POSIX storing at least -1.
    # Set it to "int" to match the behavior of AC_TYPE_SSIZE_T (autotools).
    set(SSIZE_T_DEF "typedef int ssize_t;")
endif()
check_type_size("off_t" SIZEOF_OFF_T)
if(SIZEOF_OFF_T STREQUAL "")
    # off_t is a signed type in POSIX no narrower than int.
    # Set it to "long int" to match the behavior of AC_TYPE_OFF_T (autotools).
    set(OFF_T_DEF "typedef long int off_t;")
endif()

check_type_size("int" SIZEOF_INT)
check_type_size("short" SIZEOF_SHORT)
check_type_size("long" SIZEOF_LONG)
check_type_size("long long" SIZEOF_LONG_LONG)

#
# Variables for clamav-types.h.in
#
#TODO: Move this to a .cmake file
if(HAVE_SYS_INT_TYPES_H)
    set(INT_TYPES_HEADER "#include <sys/int_types.h>")
elseif(HAVE_INTTYPES_H)
    set(INT_TYPES_HEADER "#include <inttypes.h>")
elseif(HAVE_STDINT_H)
    set(INT_TYPES_HEADER "#include <stdint.h>")
elseif(WIN32 AND MSVC)
    # Windows / Visual C++ (not Cygwin), stdint.h should exist.
    set(INT_TYPES_HEADER "#include <stdint.h>")
else()
    # No int types header available. We'll define the types manually.
    set(INT8_DEF "typedef signed char int8_t;")
    set(UINT8_DEF "typedef unsigned char uint8_t;")

    if(SIZEOF_INT EQUAL 2)
        set(INT16_DEF "typedef signed int int16_t;")
        set(UINT16_DEF "typedef unsigned int uint16_t;")
    elseif(SIZEOF_SHORT EQUAL 2)
        set(INT16_DEF "typedef signed short int16_t;")
        set(UINT16_DEF "typedef unsigned short uint16_t;")
    endif()

    if(SIZEOF_INT EQUAL 4)
        set(INT32_DEF "typedef signed int int32_t;")
        set(UINT32_DEF "typedef unsigned int uint32_t;")
    elseif(SIZEOF_LONG EQUAL 4)
        set(INT32_DEF "typedef signed long int32_t;")
        set(UINT32_DEF "typedef unsigned long uint32_t;")
    endif()

    if(SIZEOF_LONG EQUAL 8)
        set(INT64_DEF "typedef signed long int64_t;")
        set(UINT64_DEF "typedef unsigned long uint64_t;")
    elseif(SIZEOF_LONG_LONG EQUAL 8)
        set(INT64_DEF "typedef signed long long int64_t;")
        set(UINT64_DEF "typedef unsigned long long uint64_t;")
    endif()
endif()

# Always do this
if(SIZEOF_INT EQUAL 4)
    set(DEFINE_SF32_PREFIX "#define _SF32_PREFIX \"\"")
elseif(SIZEOF_LONG EQUAL 4)
    set(DEFINE_SF32_PREFIX "#define _SF32_PREFIX \"l\"")
endif()
if(SIZEOF_LONG EQUAL 8)
    set(DEFINE_SF64_PREFIX "#define _SF64_PREFIX \"l\"")
elseif(SIZEOF_LONG_LONG EQUAL 8)
    set(DEFINE_SF64_PREFIX "#define _SF64_PREFIX \"ll\"")
endif()

# Check for restrict keyword
#TODO: Move this to a .cmake file
foreach( ac_kw __restrict __restrict__ _Restrict restrict )
    check_c_source_compiles(
        "
        typedef int * int_ptr;
        int foo (int_ptr ${ac_kw} ip) {
            return ip[0];
        }
        int main() {
            int s[1];
            int * ${ac_kw} t = s;
            t[0] = 0;
            return foo(t);
        }
        "
        HAVE_RESTRICT )

    if( HAVE_RESTRICT )
        set( ac_cv_c_restrict ${ac_kw} )
        break()
    endif()
endforeach()
if( HAVE_RESTRICT )
    set( restrict ${ac_cv_c_restrict} )
endif()

# Define inline macro as needed.
include(TestInline)
# Determine if _FILE_OFFSET_BITS 64 needs to be set to handle large files.
include(CheckFileOffsetBits)
# Determine how to pack structs on this platform.
include(CheckStructPacking)
# Check for signed right shift implementation.
include(CheckSignedRightShift)
# Check if systtem fts implementation available
include(CheckFTS)
# Check if uname(2) follows POSIX standard.
include(CheckUnamePosix)
# Check support for file descriptor passing
include(CheckFDPassing)

# Check if big-endian
include(TestBigEndian)
TEST_BIG_ENDIAN(WORDS_BIGENDIAN)

include(CheckStructHasMember)
check_struct_has_member("struct tm" tm_gmtoff time.h HAVE_STRUCT_TM_TM_GMTOFF)

# Check size of pointer to decide we need 8 bytes alignment adjustment.
check_type_size("int *"   SIZEOF_INT_P)
check_type_size("time_t"  SIZEOF_TIME_T)

# Checks for library functions.
include(CheckSymbolExists)
check_symbol_exists(_Exit           "stdlib.h"      HAVE__EXIT)
check_symbol_exists(accept4         "sys/types.h"   HAVE_ACCEPT4)
check_symbol_exists(snprintf        "stdio.h"       HAVE_SNPRINTF)
check_symbol_exists(stat64          "sys/stat.h"    HAVE_STAT64)
check_symbol_exists(strcasestr      "string.h"      HAVE_STRCASESTR)
check_symbol_exists(strerror_r      "string.h"      HAVE_STRERROR_R)
check_symbol_exists(strlcat         "string.h"      HAVE_STRLCAT)
check_symbol_exists(strlcpy         "string.h"      HAVE_STRLCPY)
check_symbol_exists(strndup         "string.h"      HAVE_STRNDUP)
check_symbol_exists(strnlen         "string.h"      HAVE_STRNLEN)
check_symbol_exists(strnstr         "string.h"      HAVE_STRNSTR)
check_symbol_exists(sysctlbyname    "sysctl.h"      HAVE_SYSCTLBYNAME)
check_symbol_exists(timegm          "time.h"        HAVE_TIMEGM)
check_symbol_exists(vsnprintf       "stdio.h"       HAVE_VSNPRINTF)

if(WIN32)
    #set(HAVE_FSEEKO 1)
    set(HAVE_GETADDRINFO 1)
    set(HAVE_GETPAGESIZE 1)
    set(HAVE_MKSTEMP 1)
    set(HAVE_POLL 1)
else()
    check_symbol_exists(fseeko          "stdio.h"       HAVE_FSEEKO)
    check_symbol_exists(getaddrinfo     "netdb.h"       HAVE_GETADDRINFO)
    check_symbol_exists(getpagesize     "unistd.h"      HAVE_GETPAGESIZE)
    check_symbol_exists(mkstemp         "unistd.h"      HAVE_MKSTEMP)
    check_symbol_exists(poll            "poll.h"        HAVE_POLL)
    check_symbol_exists(setgroups       "unistd.h"      HAVE_SETGROUPS)
    check_symbol_exists(setsid          "unistd.h"      HAVE_SETSID)
endif()

include(CheckSymbolExists)
# XXX does this correctly detect initgroups (un)availability on cygwin?
check_symbol_exists(initgroups grp.h HAVE_INITGROUPS)
if(NOT HAVE_INITGROUPS AND HAVE_UNISTD_H)
    # FreeBSD declares initgroups() in unistd.h
    check_symbol_exists(initgroups unistd.h HAVE_INITGROUPS2)
    if(HAVE_INITGROUPS2)
        set(HAVE_INITGROUPS 1)
    endif()
endif()

set(WARNCFLAGS)
set(WARNCXXFLAGS)
if(CMAKE_C_COMPILER_ID MATCHES "MSVC")
    if(ENABLE_WERROR)
        set(WARNCFLAGS    /WX)
        set(WARNCXXFLAGS  /WX)
    endif()
else()
    if(ENABLE_WERROR)
        extract_valid_c_flags(WARNCFLAGS    -Werror)
        extract_valid_c_flags(WARNCXXFLAGS  -Werror)
    endif()

    # For C compiler
    extract_valid_c_flags(WARNCFLAGS
        -Wall -Wextra
        -Wformat-security
    )
    if(ENABLE_ALL_THE_WARNINGS)
        extract_valid_c_flags(WARNCFLAGS
            -Waddress
            -Wattributes
            -Wclobbered
            -Wconversion
            -Wdeclaration-after-statement
            -Wdiv-by-zero
            -Wempty-body
            -Wendif-labels
            -Wfloat-equal
            -Wformat-nonliteral
            -Winline
            -Wmissing-declarations
            -Wmissing-field-initializers
            -Wmissing-noreturn
            -Wmissing-prototypes
            -Wnested-externs
            #-Wno-format-nonliteral            # May be required to pass format string as "const char*.
            -Wpointer-arith
            -Wpragmas
            -Wredundant-decls
            -Wshadow
            -Wunreachable-code
            -Wunused-parameter
            -Wvla
            -Wwrite-strings
            -Wstrict-prototypes
            -Wundef
            -Wcast-align
            -Wextended-offsetof               # May be missing from GCC
            -Wheader-guard                    # Only work with Clang for the moment
            -Wlanguage-extension-token        # May be missing from GCC
            -Wmissing-variable-declarations   # May be missing from GCC
            #-Wpadded                          # Not used because we cannot change public structs
            -Wshorten-64-to-32                # May be missing from GCC
            -Wsign-conversion
            #-Wswitch-enum                     # Not used because this basically disallows default case
            -Wunreachable-code-break          # May be missing from GCC
            -Wunused-macros
        )
    endif()

    # For C++ compiler
    extract_valid_cxx_flags(WARNCXXFLAGS
        -Wall
        -Wformat-security
        -Wno-comment                      # Disabled because LLVM's CFG.h has a warning about a multiline comment because of ascii-art of a graph with a `\` in it.
    )
endif()

# autotools-compatible names
# Sphinx expects relative paths in the .rst files. Use the fact that the files
# below are all one directory level deep.
file(RELATIVE_PATH top_srcdir   "${CMAKE_CURRENT_BINARY_DIR}/dir" "${CMAKE_CURRENT_SOURCE_DIR}")
file(RELATIVE_PATH top_builddir "${CMAKE_CURRENT_BINARY_DIR}/dir" "${CMAKE_CURRENT_BINARY_DIR}")
set(abs_top_srcdir   "${CMAKE_CURRENT_SOURCE_DIR}")
set(abs_top_builddir "${CMAKE_CURRENT_BINARY_DIR}")
# libclamav.pc (pkg-config file)
set(prefix      "${CMAKE_INSTALL_PREFIX}")
set(exec_prefix "${CMAKE_INSTALL_PREFIX}")
set(bindir      "${CMAKE_INSTALL_FULL_BINDIR}")
set(sbindir     "${CMAKE_INSTALL_FULL_SBINDIR}")
set(libdir      "${CMAKE_INSTALL_FULL_LIBDIR}")
set(includedir  "${CMAKE_INSTALL_FULL_INCLUDEDIR}")
set(VERSION     "${PACKAGE_VERSION}")

# Absolute path of database directory
if(IS_ABSOLUTE ${DATABASE_DIRECTORY})
    set(DATADIR "${DATABASE_DIRECTORY}")
else()
    set(DATADIR "${CMAKE_INSTALL_PREFIX}/${DATABASE_DIRECTORY}")
endif()
# Absolute path of the applications' config directory
if(IS_ABSOLUTE ${APP_CONFIG_DIRECTORY})
    set(CONFDIR "${APP_CONFIG_DIRECTORY}")
else()
    set(CONFDIR "${CMAKE_INSTALL_PREFIX}/${APP_CONFIG_DIRECTORY}")
endif()

if(ENABLE_DEBUG)
    set(CL_DEBUG 1)
endif()
if(ENABLE_EXPERIMENTAL)
    set(CL_EXPERIMENTAL 1)
endif()
if(ENABLE_STRN_INTERNAL)
    set(HAVE_STRNI 1)
endif()
if(ENABLE_FRESHCLAM_DNS_FIX)
    set(FRESHCLAM_DNS_FIX 1)
endif()
if(ENABLE_FRESHCLAM_NO_CACHE)
    set(FRESHCLAM_NO_CACHE 1)
endif()

set(SCANBUFF 131072) # scan buffer size
set(FILEBUFF 8192)   # file i/o buffer size

if(APPLE)
    set(C_DARWIN 1)
endif()

include(CheckFmapFeatures)
if(WIN32)
    set(USE_MPOOL 1)
endif()

add_definitions(-DHAVE_CONFIG_H)
configure_file(clamav-config.h.cmake.in clamav-config.h)
configure_file(target.h.cmake.in target.h)
configure_file(platform.h.in platform.h)
configure_file(clamav-version.h.in clamav-version.h)
configure_file(clamav-types.h.in clamav-types.h)

if(WIN32)
    #
    # Windows-specific config stuff
    #

    # Windows resource file
    set(CLAMAV_RES "")
    configure_file(
        win32/res/common.rc.in
        ${CMAKE_BINARY_DIR}/version.rc
        @ONLY)
else()
    #
    # POSIX-specific config stuff
    #

    # Don't confuse clamav-config.in with clamav-config.h.in or clamav-config.h.cmake.in
    configure_file(clamav-config.in
        ${CMAKE_CURRENT_BINARY_DIR}/clamav-config
        @ONLY)
    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/clamav-config"
            DESTINATION "${CMAKE_INSTALL_BINDIR}"
        PERMISSIONS
            OWNER_READ OWNER_WRITE OWNER_EXECUTE
            GROUP_READ             GROUP_EXECUTE
            WORLD_READ             WORLD_EXECUTE
        COMPONENT libraries)

    # pkg-config
    configure_file(
        libclamav.pc.in
        ${CMAKE_CURRENT_BINARY_DIR}/libclamav.pc
        @ONLY)
    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libclamav.pc"
        DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
        COMPONENT libraries)
endif()

# include CTest to enable testing before we add any subdirectories which may
# add additional tests (like with `add_rust_test()`)
if(ENABLE_TESTS)
    include(CTest)
endif()

#
# ClamAV Build targets!
#

# Build targets for libraries.
if(ENABLE_UNRAR)
    # Only build libclamunrar if enabled.
    # We still define the libclamunrar_iface's interface regardless,
    # so that libclamav will have unrar_iface.h in the include path.
    add_subdirectory( libclamunrar )
endif()
add_subdirectory( libclamunrar_iface )

if(NOT ENABLE_EXTERNAL_MSPACK)
    add_subdirectory( libclammspack )
    set(LIBMSPACK "ClamAV::libmspack")
else()
    find_package(MSPack REQUIRED)
    set(LIBMSPACK "MSPack::mspack")
endif()

if(WIN32)
    add_subdirectory( win32/compat )
endif()

add_subdirectory( libclamav )
add_subdirectory( libclamav_rust )

if(NOT ENABLE_LIBCLAMAV_ONLY)
    add_subdirectory( common )

    add_subdirectory( libfreshclam )

    if(ENABLE_APP)
        # Build targets for primary applications.
        add_subdirectory( clamconf )

        add_subdirectory( clamd )

        add_subdirectory( clamdscan )

        if(C_LINUX AND ENABLE_CLAMONACC)
            add_subdirectory( clamonacc )
        endif()

        if(ENABLE_MILTER)
            add_subdirectory( clamav-milter )
        endif()

        add_subdirectory( clamscan )

        add_subdirectory( sigtool )

        add_subdirectory( clambc )

        add_subdirectory( clamsubmit )

        add_subdirectory( freshclam )

        add_subdirectory( clamdtop )

        if(WIN32)
            add_subdirectory( win32/conf_examples )
        else()
            add_subdirectory( etc )
        endif()
    endif()
endif()

if(ENABLE_EXAMPLES)
    add_subdirectory( examples )
endif()

add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})
if(ENABLE_TESTS)
    add_subdirectory( unit_tests )
endif()

add_subdirectory( docs )

if(ENABLE_FUZZ)
    add_subdirectory( fuzz )
endif()

if(WIN32)
    # Include the license(s) in the installation
    install(FILES ${CMAKE_SOURCE_DIR}/COPYING.txt DESTINATION . COMPONENT libraries)
    install(DIRECTORY ${CMAKE_SOURCE_DIR}/COPYING DESTINATION . COMPONENT libraries)
endif()

#
# The Summary Info.
#
include(ColourMessage)
string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type)
message(STATUS "${Y}Configuration Options Summary${e} --
${c}    Package Version:        ${e}${PACKAGE_STRING}
${c}    libclamav version:      ${e}${LIBCLAMAV_CURRENT}:${LIBCLAMAV_REVISION}:${LIBCLAMAV_AGE}
${c}    libfreshclam version:   ${e}${LIBFRESHCLAM_CURRENT}:${LIBFRESHCLAM_REVISION}:${LIBFRESHCLAM_AGE}
${c}    Install prefix:         ${e}${CMAKE_INSTALL_PREFIX}
${c}    Install database dir:   ${e}${DATADIR}
${c}    Install config dir:     ${e}${CONFDIR}
${c}    Host system:            ${e}${CMAKE_HOST_SYSTEM}
${c}    Target system:          ${e}${CMAKE_SYSTEM}
${c}    Compiler:               ${e}
${b}        Build type:         ${e}${CMAKE_BUILD_TYPE}
${b}        C compiler:         ${e}${CMAKE_C_COMPILER}
${b}        C++ compiler:       ${e}${CMAKE_CXX_COMPILER}
${b}        Rust toolchain:     ${e}${cargo_EXECUTABLE} (${cargo_VERSION})
${b}        CFLAGS:             ${e}${CMAKE_C_FLAGS_${_build_type}} ${CMAKE_C_FLAGS}
${b}        CXXFLAGS:           ${e}${CMAKE_CXX_FLAGS_${_build_type}} ${CMAKE_CXX_FLAGS}
${b}        WARNCFLAGS:         ${e}${WARNCFLAGS}")
if(VCPKG_TOOLCHAIN)
message("\
${c}    Using vcpkg:            ${e}
${b}        Target Triplet:     ${e}${VCPKG_TARGET_TRIPLET}
${b}        Target Arch:        ${e}${VCPKG_TARGET_ARCHITECTURE}
${b}        Get runtime DLLs:   ${e}${VCPKG_APPLOCAL_DEPS}
${b}        Package root path:  ${e}${VCPKG_CMAKE_FIND_ROOT_PATH}")
endif()
message("\
${c}    Build Options:          ${e}
${b}        Build apps:         ${e}${ENABLE_APP}
${b}        Shared library:     ${e}${ENABLE_SHARED_LIB}
${b}        Static library:     ${e}${ENABLE_STATIC_LIB}
${b}        Enable UnRAR:       ${e}${ENABLE_UNRAR}
${b}        Examples:           ${e}${ENABLE_EXAMPLES}
${b}        Tests:              ${e}${ENABLE_TESTS}
${b}        Build man pages:    ${e}${ENABLE_MAN_PAGES}
${b}        Build doxygen HTML: ${e}${ENABLE_DOXYGEN}
${b}        Maintainer Mode:    ${e}${MAINTAINER_MODE}")
if(MAINTAINER_MODE)
message("\
${R}    Maintainer Mode Tools:  ${e}
${y}        bison:              ${e}${BISON_EXECUTABLE} (${BISON_VERSION})
${y}        flex:               ${e}${FLEX_EXECUTABLE} (${FLEX_VERSION})")
endif()
if(NOT WIN32)
message("\
${c}    Build Extras:           ${e}")
message("\
${b}        Build milter:       ${e}${ENABLE_MILTER}  (toggle with -DENABLE_MILTER=ON/OFF)")
if(C_LINUX)
message("\
${b}        Build clamonacc:    ${e}${ENABLE_CLAMONACC}  (toggle with -DENABLE_CLAMONACC=ON/OFF)")
endif()
endif()

if(LLVM_FOUND)
message(STATUS "${C}Engine Options${e} --
${b}        Bytecode Runtime:   ${e}
${_}            llvm            ${e}${LLVM_INCLUDE_DIRS}
${_}                            ${e}${LLVM_LIBRARY_DIRS}
${_}                            ${e}${LLVM_LIBRARIES}")
else()
message(STATUS "${C}Engine Options${e} --
${b}        Bytecode Runtime:   ${e}
${_}            ${BYTECODE_RUNTIME}            ${e}")
endif()
if(ENABLE_TESTS)
message(STATUS "${C}Test Dependencies${e} --
${b}        Unit Test Framework:${e}
${_}            libcheck        ${e}${LIBCHECK_INCLUDE_DIR}
${_}                            ${e}${LIBCHECK_LIBRARY}
${b}        Feature Test Framework:${e}
${_}            python3         ${e}${Python3_EXECUTABLE}
${_}            test command    ${e}${PythonTest_COMMAND}")
endif()
message(STATUS "${C}libclamav Dependencies${e} --
${b}        Compression support:${e}
${_}            bzip2           ${e}${BZIP2_INCLUDE_DIRS}
${_}                            ${e}${BZIP2_LIBRARIES}
${_}            zlib            ${e}${ZLIB_INCLUDE_DIRS}
${_}                            ${e}${ZLIB_LIBRARIES}
${b}        XML support:        ${e}
${_}            libxml2         ${e}${LIBXML2_INCLUDE_DIRS}
${_}                            ${e}${LIBXML2_LIBRARIES}
${b}        RegEx support:      ${e}
${_}            libpcre2        ${e}${PCRE2_INCLUDE_DIRS}
${_}                            ${e}${PCRE2_LIBRARIES}
${b}        Crypto support:     ${e}
${_}            openssl         ${e}${OPENSSL_INCLUDE_DIR}
${_}                            ${e}${OPENSSL_LIBRARIES}
${b}        JSON support:       ${e}
${_}            json-c          ${e}${JSONC_INCLUDE_DIRS}
${_}                            ${e}${JSONC_LIBRARIES}
${b}        Threading support:  ${e}")
if(WIN32)
message("\
${_}            pthread-win32   ${e}${PThreadW32_INCLUDE_DIRS}
${_}                            ${e}${PThreadW32_LIBRARIES}")
elseif(Threads_FOUND AND CMAKE_USE_PTHREADS_INIT)
message("\
${_}            pthread         ${e}")
else()
message("\
${o}            no              ${e}")
endif()
if(NOT WIN32)
message("\
${b}        Locale support:     ${e}
${_}            iconv           ${e}${Iconv_INCLUDE_DIRS}
${_}                            ${e}${Iconv_LIBRARIES}")
endif()

if(NOT ENABLE_LIBCLAMAV_ONLY)
message(STATUS "${C}libfreshclam Extra Dependencies${e} --
${b}        HTTP support:       ${e}
${_}            curl            ${e}${CURL_INCLUDE_DIRS}
${_}                            ${e}${CURL_LIBRARIES}")
endif()

if(HAVE_LIBNCURSES)
message(STATUS "${C}Application Extra Dependencies${e} --
${b}        GUI support:        ${e}
${_}            ncurses         ${e}${CURSES_INCLUDE_DIRS}
${_}                            ${e}${CURSES_LIBRARIES}")
elseif(HAVE_LIBPDCURSES)
message(STATUS "${C}Application Extra Dependencies${e} --
${b}        GUI support:        ${e}
${_}            pdcurses        ${e}${CURSES_INCLUDE_DIRS}
${_}                            ${e}${CURSES_LIBRARIES}")
endif()

if(C_LINUX)
if(SYSTEMD_PROGRAM_FOUND)
message("\
${b}        systemd:            ${e}
${_}            unit directory  ${e}${SYSTEMD_UNIT_DIR}
${b}        systemd ctl support: ${e}")
if(SYSTEMD_FOUND)
message("\
${_}            libsystemd      ${e}${SYSTEMD_INCLUDE_DIRS}
${_}                            ${e}${SYSTEMD_LIBRARIES}")
else()
message("\
${_}            libsystemd      ${e}not found")
endif()
else()
message("\
${b}        systemd:            ${e}not found")
endif()
endif()

if(ENABLE_MILTER)
message("\
${b}        Milter Support:     ${e}
${_}            libmilter       ${e}${Milter_INCLUDE_DIRS}
${_}                            ${e}${Milter_LIBRARIES}")
endif()
message("")

if(NOT JSONC_USE_STATIC)
    message(STATUS "${g}Warning:${e} libjson-c is known to share symbol names with other JSON libraries which may result in crashes for applications that use libclamav. Consider providing a static json-c library that was compiled with: CFLAGS=\"-fPIC\". Default build settings for json-c 0.15+ should also work. Use the `-DENABLE_JSON_SHARED=OFF` option to prefer detection of the static library, or use -DJSONC_INCLUDE_DIR and -DJSONC_LIBRARY to specify the static JSON library.")
endif()