CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
PROJECT(black-hole-solver)

INCLUDE ("${CMAKE_SOURCE_DIR}/cmake/shlomif_common_bootstrap.cmake")
SHLOMIF_COMMON_SETUP("${private_mod_path}")
INCLUDE(GNUInstallDirs)

CMAKE_POLICY(SET CMP0054 NEW)
option (FCS_WITH_TEST_SUITE "Also build and run the test suite." ON)
option (FCS_AVOID_TCMALLOC "Avoid linking against Google's tcmalloc")
option (USE_SYSTEM_XXHASH "Link to the system's xxHash instead of the bundled copy")
option (BUILD_STATIC_LIBRARY "Build and install the static library")
option (LINK_TO_STATIC "Link the executables to the static library")
option (DISABLE_APPLYING_RPATH "Disable applying rpath")
option (ENABLE_DISPLAYING_MAX_NUM_PLAYED_CARDS "Allow recording and displaying the maximal number of played/\"moved\" cards. Enabling it may make the non-related run time somewhat slower.")

INCLUDE(FindPkgConfig)

# Introduces VERSION , CPACK_PACKAGE_VERSION_MAJOR,
# CPACK_PACKAGE_VERSION_MAJOR, and CPACK_PACKAGE_VERSION_PATCH
READ_VERSION_FROM_VER_TXT()

# This is the equivalent to perform a "make dist"/"make distdir" etc.
SET(CPACK_PACKAGE_NAME "black-hole-solver")
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Black Hole Solver")
SET(CPACK_PACKAGE_VENDOR "Shlomi Fish")
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING")

SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_DESCRIPTION_SUMMARY} ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")

SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")

SET (BHS_STATE_STORAGE "BHS_STATE_STORAGE_INTERNAL_HASH" CACHE STRING "The State Storage Type")
SET (FCS_IA_PACK_SIZE 64 CACHE STRING "Size of a single pack in kilo-bytes.")
SET (IA_STATE_PACKS_GROW_BY 32 CACHE STRING "Amount to Grow State Packs By")
SET (base_with_ver "black_hole_solver-[0-9]+\\\\.[0-9]+\\\\.[0-9]+")
SET(CPACK_SOURCE_GENERATOR "TXZ")
SET(CPACK_SOURCE_IGNORE_FILES
    "/\\\\.git/"
    "/\\\\.tidyall\\\\.d/"
    "/tags$"
    "\\\\.swp$"
    "ids-whitelist\\\\.txt"
    "~$"
)

IF (NOT DISABLE_APPLYING_RPATH)
    ### This is to set the RPATH correctly, so when installed under a prefix
    ### the executables will find the libraries.
    ###
    ### See:
    ###
    ### http://www.cmake.org/Wiki/CMake_RPATH_handling
    ###
    ### (Taken from that wiki page)

    # use, i.e. don't skip the full RPATH for the build tree
    SET(CMAKE_SKIP_BUILD_RPATH  FALSE)

    # when building, don't use the install RPATH already
    # (but later on when installing)
    SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)

    # the RPATH to be used when installing
    SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}")

    # add the automatically determined parts of the RPATH
    # which point to directories outside the build tree to the install RPATH
    SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
ENDIF ()
SET (LIB_BASE "black_hole_solver")
SET (BLACK_HOLE_SOLVER_MODULES "can_move.c" "generated/lib.c" "meta_alloc.c" "rank_reach_prune.c")

IF (${BHS_STATE_STORAGE} STREQUAL "BHS_STATE_STORAGE_INTERNAL_HASH")
    LIST(APPEND BLACK_HOLE_SOLVER_MODULES fcs_hash.c)
ENDIF ()

SET (DEP_LIBS)

IF (${BHS_STATE_STORAGE} STREQUAL "BHS_STATE_STORAGE_TOKYO_CAB_HASH")
    PKG_CHECK_MODULES(TOKYO_CAB REQUIRED "tokyocabinet")

    IF (NOT TOKYO_CAB_FOUND)
        MESSAGE (FATAL "Tokyo cabinet could not be found.")
    ENDIF ()

    SET (DEP_LIBS ${TOKYO_CAB_LIBRARIES})
ENDIF ()

IF (USE_SYSTEM_XXHASH)
    LIST(APPEND DEP_LIBS "xxhash")
ENDIF ()

INCLUDE ("${CMAKE_SOURCE_DIR}/cmake/rinutils_bootstrap.cmake")

RINUTILS_SET_UP_FLAGS()

SET(LIBTCMALLOC_LIB_LIST)

# Avoid linking to tcmalloc if the test suite is enabled due to:
# https://github.com/gperftools/gperftools/issues/758
IF ((NOT (FCS_AVOID_TCMALLOC)) AND (NOT (FCS_WITH_TEST_SUITE)) AND ( NOT ((CMAKE_BUILD_TYPE STREQUAL "debug") OR (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") ) ))
    # Optionally link against Google's TCMalloc if it's available:
    # http://goog-perftools.sourceforge.net/
    # This gives better performance for the threaded programs.
    FIND_LIBRARY(LIBTCMALLOC_LIB tcmalloc)

    IF(NOT ("${LIBTCMALLOC_LIB}" STREQUAL "LIBTCMALLOC_LIB-NOTFOUND"))
        SET(LIBTCMALLOC_LIB_LIST ${LIBTCMALLOC_LIB})
    ENDIF()
ENDIF ()

include(TestBigEndian)
TEST_BIG_ENDIAN(BIG_ENDIAN)
IF (BIG_ENDIAN)
    ADD_DEFINITIONS("-DXXH_CPU_LITTLE_ENDIAN=0")
ELSE ()
    ADD_DEFINITIONS("-DXXH_CPU_LITTLE_ENDIAN=1")
ENDIF ()

# Add the google_hash.cpp if (and only if) it is being used.
#
IF (${BHS_STATE_STORAGE} STREQUAL "BHS_STATE_STORAGE_GOOGLE_SPARSE_HASH")
    LIST(APPEND BLACK_HOLE_SOLVER_MODULES google_hash.cpp)
ENDIF ()

INCLUDE(CPack)

SHLOMIF_ADD_COMMON_C_FLAGS()

SHLOMIF_FINALIZE_FLAGS()

IF ("$ENV{FCS_CLANG}")
    ADD_DEFINITIONS("-Weverything -Wno-language-extension-token -Wno-gnu-statement-expression -Wno-used-but-marked-unused -Wno-padded -Wno-cast-align -Wno-extra-semi-stmt")
ENDIF ()

IF ("$ENV{FCS_GCC}")
    ADD_DEFINITIONS("-W -Waddress -Waggressive-loop-optimizations -Wall -Wattributes -Wbad-function-cast -Wbool-compare -Wbool-operation -Wbuiltin-declaration-mismatch -Wbuiltin-macro-redefined -Wcast-align -Wchar-subscripts -Wchkp -Wclobbered -Wcomment -Wcomments -Wcoverage-mismatch -Wcpp -Wdangling-else -Wdate-time -Wdeprecated -Wdeprecated-declarations -Wdesignated-init -Wdisabled-optimization -Wdiscarded-array-qualifiers -Wdiscarded-qualifiers -Wdiv-by-zero -Wdouble-promotion -Wduplicated-branches -Wduplicated-cond -Wduplicate-decl-specifier -Wempty-body -Wendif-labels -Wenum-compare -Wexpansion-to-defined -Wextra -Wformat-contains-nul -Wformat-extra-args -Wformat-nonliteral -Wformat-security -Wformat-signedness -Wformat-y2k -Wformat-zero-length -Wframe-address -Wfree-nonheap-object -Whsa -Wignored-attributes -Wignored-qualifiers -Wimplicit -Wimplicit-function-declaration -Wimplicit-int -Wincompatible-pointer-types -Winit-self -Winline -Wint-conversion -Wint-in-bool-context -Wint-to-pointer-cast -Winvalid-memory-model -Winvalid-pch -Wjump-misses-init -Wlogical-not-parentheses -Wlogical-op -Wmain -Wmaybe-uninitialized -Wmemset-elt-size -Wmemset-transposed-args -Wmisleading-indentation -Wmissing-braces -Wmissing-declarations -Wmissing-field-initializers -Wmissing-include-dirs -Wmissing-parameter-type -Wmissing-prototypes -Wmultichar -Wnarrowing -Wnested-externs -Wnonnull -Wnonnull-compare -Wnull-dereference -Wodr -Wold-style-declaration -Wold-style-definition -Wopenmp-simd -Woverflow -Woverlength-strings -Woverride-init -Wpacked -Wpacked-bitfield-compat -Wparentheses -Wpointer-arith -Wpointer-compare -Wpointer-sign -Wpointer-to-int-cast -Wpragmas -Wpsabi -Wrestrict -Wreturn-local-addr -Wreturn-type -Wscalar-storage-order -Wsequence-point -Wshadow -Wshift-count-negative -Wshift-count-overflow -Wshift-negative-value -Wsizeof-array-argument -Wsizeof-pointer-memaccess -Wstack-protector -Wstrict-aliasing -Wstrict-prototypes -Wsuggest-attribute=const -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wsuggest-attribute=pure -Wsuggest-final-methods -Wsuggest-final-types -Wswitch -Wswitch-bool -Wswitch-default -Wswitch-unreachable -Wsync-nand -Wtautological-compare -Wtrampolines -Wtrigraphs -Wtype-limits -Wuninitialized -Wunsafe-loop-optimizations -Wunused -Wunused-but-set-parameter -Wunused-but-set-variable -Wunused-function -Wunused-label -Wunused-local-typedefs -Wunused-macros -Wunused-parameter -Wunused-result -Wunused-value -Wunused-variable -Wvarargs -Wvariadic-macros -Wvector-operation-performance -Wvla -Wvolatile-register-var -Wwrite-strings -Walloc-size-larger-than=9223372036854775807 -Warray-bounds=2 -Wformat-overflow=2 -Wformat-truncation=2 -Wnormalized=nfc -Wshift-overflow=2 -Wstringop-overflow=2 -Wunused-const-variable=2 -Wstrict-overflow=1  -Wno-switch-default -Wno-vla -Wno-inline  -Wno-jump-misses-init -Wno-unused-result -Wno-unsafe-loop-optimizations")
    # -Wimplicit-fallthrough=5
    # -Wno-vla-larger-than

    IF (NOT IS_DEBUG)
        ADD_DEFINITIONS("-D_FORTIFY_SOURCE=2")
    ENDIF ()
ENDIF ()
ADD_DEFINITIONS("-Wno-unknown-pragmas")

# So it can find config.h
INCLUDE_DIRECTORIES(BEFORE "${CMAKE_CURRENT_SOURCE_DIR}")
INCLUDE_DIRECTORIES(BEFORE "${CMAKE_CURRENT_BINARY_DIR}")
INCLUDE_DIRECTORIES(BEFORE "${CMAKE_CURRENT_BINARY_DIR}/generated")
INCLUDE_DIRECTORIES(BEFORE "${CMAKE_CURRENT_BINARY_DIR}/generated/include")
INCLUDE_DIRECTORIES(BEFORE "${CMAKE_CURRENT_SOURCE_DIR}/include")

SET (is_parent_gen "${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen-c-lookup-files.pl")
SET (is_parent_args "${is_parent_gen}")
SET (is_parent_output
    "${CMAKE_CURRENT_BINARY_DIR}/can_move.c" "${CMAKE_CURRENT_BINARY_DIR}/can_move.h"
)
ADD_CUSTOM_COMMAND(
    OUTPUT ${is_parent_output}
    COMMAND "${PERL_EXECUTABLE}"
    ARGS ${is_parent_args}
    DEPENDS "${is_parent_gen}"
)
# Execute it right away to avoid missing files/includes
SET(cmd "${PERL_EXECUTABLE}" ${is_parent_args})
EXECUTE_PROCESS(COMMAND ${cmd} RESULT_VARIABLE ret)
IF (NOT ("${ret}" STREQUAL "0"))
    MESSAGE(FATAL_ERROR "${cmd} failed!")
ENDIF ()

IF (ENABLE_DISPLAYING_MAX_NUM_PLAYED_CARDS)
    SET(process_args "--process=none")
ELSE ()
    SET(process_args "--process=no_max_num_played")
ENDIF ()

FIND_PROGRAM(_PYTHON3 "python3")
SET (found_python3 )
IF (NOT "${_PYTHON3}" STREQUAL "_PYTHON3-NOTFOUND")
    SET (found_python3 "1")
    SET (PYTHON3_EXECUTABLE "python3")
ENDIF ()
SET(py3_prog "${CMAKE_CURRENT_SOURCE_DIR}/scripts/source_filter.py")
SET(cmd "${PYTHON3_EXECUTABLE}" "${py3_prog}" ${process_args})
SET(sources)
SET(dests)
FOREACH (src "lib.c" "solver_common.h" "include/black-hole-solver/black_hole_solver.h")
    LIST(APPEND sources "${CMAKE_CURRENT_SOURCE_DIR}/${src}")
    LIST(APPEND dests "${CMAKE_CURRENT_BINARY_DIR}/generated/${src}")
ENDFOREACH()
ADD_CUSTOM_COMMAND(
    OUTPUT ${dests}
    COMMAND ${cmd}
    DEPENDS "${py3_prog}" ${sources}
)
EXECUTE_PROCESS(COMMAND ${cmd} RESULT_VARIABLE ret)
IF (NOT ("${ret}" STREQUAL "0"))
    MESSAGE(FATAL_ERROR "${cmd} failed!")
ENDIF ()
ADD_LIBRARY (
    "${LIB_BASE}"
    SHARED
    ${BLACK_HOLE_SOLVER_MODULES}
)

SET_TARGET_PROPERTIES(
    "${LIB_BASE}" PROPERTIES VERSION 1.0.1 SOVERSION 1
)

SET (LIBS "${LIB_BASE}")
SET (LINKLIB "${LIB_BASE}")

IF (BUILD_STATIC_LIBRARY)
    SET (S_LIB "${LIB_BASE}-static")
    ADD_LIBRARY (
        "${S_LIB}"
        STATIC
        ${BLACK_HOLE_SOLVER_MODULES}
    )
    SET_TARGET_PROPERTIES(
        "${S_LIB}"
        PROPERTIES OUTPUT_NAME "${LIB_BASE}"
    )
    LIST(APPEND LIBS "${S_LIB}")
    IF (LINK_TO_STATIC)
        SET (LINKLIB "${S_LIB}")
    ENDIF ()
ENDIF ()

SET_TARGET_PROPERTIES(
    ${LIBS}
    PROPERTIES CLEAN_DIRECT_OUTPUT 1
)

ADD_LIBRARY (
    "bhs_rank_reach_prune"
    SHARED
    "rank_reach_prune.c"
)

INSTALL (
    TARGETS ${LIBS}
    LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
    ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
)

TARGET_LINK_LIBRARIES("${LIB_BASE}" ${DEP_LIBS})

IF (WIN32 AND NOT UNIX)
    # This is so on Windows, the .dll's will be installed in the bin/
    # directory as well where the command-line utilities
    # will be able to find them.

    INSTALL(
        TARGETS "${LIB_BASE}"
        DESTINATION "${CMAKE_INSTALL_BINDIR}"
    )
ENDIF ()

IF (NOT USE_SYSTEM_XXHASH)
    INCLUDE("${private_mod_path}/xxhash_wrapper_bootstrap.cmake")
ENDIF ()

SET(AUTOGENERATED_CONFIG_H "config.h was auto-generated from config.h.in . Do not modify directly")

INCLUDE (CheckTypeSize)
CHECK_TYPE_SIZE("int" INT_SIZE_IN_BYTES)
CHECK_TYPE_SIZE("void*" SIZEOF_VOID_P BUILTIN_TYPES_ONLY)

SET (BHS_EXE "black-hole-solve")

ADD_EXECUTABLE(
    "${BHS_EXE}"
    "single_board_main.c"
)

INSTALL (TARGETS "${BHS_EXE}" DESTINATION "${CMAKE_INSTALL_BINDIR}")

ADD_EXECUTABLE(
    "multi-bhs-solver"
    "multi_solver.c"
)
SET(exes  "${BHS_EXE}" "multi-bhs-solver")
IF (ENABLE_DISPLAYING_MAX_NUM_PLAYED_CARDS)
    ADD_EXECUTABLE(
        "stats-multi-bhs-solver"
        "multi_solver_stats.c"
    )
    LIST(APPEND exes "stats-multi-bhs-solver")
ENDIF ()

FOREACH (exe ${exes})
    TARGET_LINK_LIBRARIES(
        "${exe}"
        "${LINKLIB}"
        ${LIBTCMALLOC_LIB_LIST}
    )
ENDFOREACH()

INSTALL(
    FILES
        "include/black-hole-solver/black_hole_solver.h"
        "include/black-hole-solver/bool.h"
        "include/black-hole-solver/fcs_dllexport.h"
    DESTINATION
        "${CMAKE_INSTALL_INCLUDEDIR}/black-hole-solver"
)

INSTALL(
    FILES
        "${CMAKE_CURRENT_BINARY_DIR}/lib${CPACK_PACKAGE_NAME}.pc"
    DESTINATION
        "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
)

CONFIGURE_FILE(
    ${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
    ${CMAKE_CURRENT_BINARY_DIR}/config.h
)

CONFIGURE_FILE(
    ${CMAKE_CURRENT_SOURCE_DIR}/${CPACK_PACKAGE_NAME}.spec.in
    ${CMAKE_CURRENT_SOURCE_DIR}/${CPACK_PACKAGE_NAME}.spec
)

CONFIGURE_FILE(
    ${CMAKE_CURRENT_SOURCE_DIR}/lib${CPACK_PACKAGE_NAME}.pc.in
    ${CMAKE_CURRENT_BINARY_DIR}/lib${CPACK_PACKAGE_NAME}.pc
    @ONLY
)

# Rebuild config.h if ver.txt has changed.
ADD_CUSTOM_COMMAND(
    OUTPUT "config.h.in"
    DEPENDS "ver.txt"
    COMMAND "touch"
    ARGS "config.h.in"
)

ENABLE_TESTING()

SET (test_tags)
IF (NOT ENABLE_DISPLAYING_MAX_NUM_PLAYED_CARDS)
    LIST(APPEND test_tags ":no_max_num_played")
ENDIF ()
FILE (WRITE "${CMAKE_CURRENT_BINARY_DIR}/TEST_TAGS.txt" "${test_tags}")
ADD_TEST(
    NAME perl_run_tests
    COMMAND "perl" "${CMAKE_CURRENT_SOURCE_DIR}/run-tests.pl"
)
ADD_CUSTOM_TARGET(
    "check"
    "perl" "${CMAKE_CURRENT_SOURCE_DIR}/run-tests.pl"
)

ADD_SUBDIRECTORY ("man")
