cmake_minimum_required(VERSION 3.16)

file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/CHANGES" _libowfat_changes LIMIT_COUNT 1)
string(REGEX REPLACE ":.*$" "" LIBOWFAT_VERSION "${_libowfat_changes}")
if(NOT LIBOWFAT_VERSION MATCHES "^[0-9]+(\\.[0-9]+)*$")
  message(FATAL_ERROR "Could not read libowfat version from first line of CHANGES")
endif()

project(libowfat
  VERSION ${LIBOWFAT_VERSION}
  DESCRIPTION "libowfat build"
  LANGUAGES C)

include(CheckCSourceCompiles)
include(CheckIncludeFile)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

option(LIBOWFAT_BUILD_STATIC "Build static libowfat/nwowfat library" OFF)
option(LIBOWFAT_BUILD_SHARED "Build shared libowfat library" ON)
set(LIBOWFAT_LIBRARY_PREFIX "" CACHE STRING "Optional library basename prefix, e.g. nw builds libnwowfat")
option(LIBOWFAT_INSTALL "Install libraries and headers" ON)
option(LIBOWFAT_GLIBC_COMPAT "Enable glibc-compatible feature-test defines" ON)

if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
  include(CTest)
  set(_libowfat_testing_default ${BUILD_TESTING})
else()
  set(_libowfat_testing_default OFF)
endif()
option(LIBOWFAT_BUILD_TEST_PROGRAMS "Build the programs in the test directory" ${_libowfat_testing_default})
option(LIBOWFAT_RUN_SELF_TESTS "Register non-interactive tests with CTest" ${_libowfat_testing_default})
if(LIBOWFAT_RUN_SELF_TESTS)
  enable_testing()
endif()

if(NOT LIBOWFAT_BUILD_STATIC AND NOT LIBOWFAT_BUILD_SHARED)
  message(FATAL_ERROR "At least one of LIBOWFAT_BUILD_STATIC or LIBOWFAT_BUILD_SHARED must be ON")
endif()

# Keep this CMake build close to the existing GNUmakefile, but make the checks
# out-of-tree friendly and avoid dietlibc-specific defaults.  glibc builds get
# _GNU_SOURCE so Linux/glibc extension declarations stay visible.
set(LIBOWFAT_FEATURE_DEFINES _REENTRANT)
if(LIBOWFAT_GLIBC_COMPAT AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
  check_c_source_compiles("#ifndef _GNU_SOURCE\n#error _GNU_SOURCE is not predefined\n#endif\nint main(void) { return 0; }" LIBOWFAT_GNU_SOURCE_PREDEFINED)
  if(NOT LIBOWFAT_GNU_SOURCE_PREDEFINED)
    list(APPEND LIBOWFAT_FEATURE_DEFINES _GNU_SOURCE)
  endif()
endif()
set(LIBOWFAT_OUTPUT_NAME "${LIBOWFAT_LIBRARY_PREFIX}owfat")
set(LIBOWFAT_INSTALL_INCLUDE_SUBDIR "${LIBOWFAT_LIBRARY_PREFIX}libowfat" CACHE STRING "Header install subdirectory below CMAKE_INSTALL_INCLUDEDIR. Defaults to libowfat or <prefix>libowfat")
set(LIBOWFAT_BUILD_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include")
set(LIBOWFAT_BUILD_PUBLIC_INCLUDE_DIR "${LIBOWFAT_BUILD_INCLUDE_DIR}/${LIBOWFAT_INSTALL_INCLUDE_SUBDIR}")

set(CMAKE_REQUIRED_DEFINITIONS "")
foreach(_def IN LISTS LIBOWFAT_FEATURE_DEFINES)
  list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D${_def}")
endforeach()
set(CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}")

check_c_source_compiles("#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\nint main(void) { struct sockaddr_in6 x; (void)x; return 0; }" LIBC_HAS_IP6)
check_c_source_compiles("#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\nint main(void) { struct sockaddr_in6 x; x.sin6_scope_id = 0; return 0; }" LIBC_HAS_SCOPE_ID)
check_c_source_compiles("#include <sys/socket.h>\nint main(void) { socklen_t x = 0; return (int)x; }" HAVE_SOCKLEN_T)
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/tryn2i.c" _tryn2i_source)
check_c_source_compiles("${_tryn2i_source}" HAVE_N2I)
check_c_source_compiles("static inline int f(void) { return 0; }\nint main(void) { return f(); }" HAVE_INLINE)
check_c_source_compiles("#include <sys/types.h>\n#include <sys/event.h>\nint main(void) { return kqueue(); }" HAVE_KQUEUE)
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/trybsdsf.c" _trybsdsf_source)
check_c_source_compiles("${_trybsdsf_source}" HAVE_BSDSENDFILE)
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/trysendfile.c" _trysendfile_source)
check_c_source_compiles("${_trysendfile_source}" HAVE_SENDFILE)
check_c_source_compiles("#include <sys/epoll.h>\nint main(void) { return epoll_create(1) < 0; }" HAVE_EPOLL)
check_c_source_compiles("#include <sys/devpoll.h>\nint main(void) { return 0; }" HAVE_DEVPOLL)
check_c_source_compiles("#include <signal.h>\n#include <fcntl.h>\nint main(void) { int x = F_SETSIG; return x == 0; }" HAVE_SIGIO)
check_c_source_compiles("int main(void) { unsigned __int128 x = 0; return (int)x; }" HAVE_UINT128)
check_c_source_compiles("#include <unistd.h>\nint main(void) { char c; return pread(0, &c, 1, 0) < 0; }" HAVE_PREAD)
check_c_source_compiles("#include <fcntl.h>\n#include <sys/socket.h>\nint main(void) { return accept4(-1, 0, 0, SOCK_CLOEXEC); }" HAVE_ACCEPT4)
check_c_source_compiles("#include <poll.h>\nint main(void) { struct pollfd p; return poll(&p, 0, 0); }" HAVE_POLL)
check_c_source_compiles("#include <sys/select.h>\nint main(void) { fd_set fds; FD_ZERO(&fds); return 0; }" HAVE_SYS_SELECT_H)
check_c_source_compiles("#include <stdlib.h>\n#include <alloca.h>\nint main(void) { void *p = alloca(1); return p == 0; }" HAVE_ALLOCA_H)
check_c_source_compiles("#include <stdlib.h>\n#include <malloc.h>\nint main(void) { return 0; }" HAVE_MALLOC_H)

set(_generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
set(_generated_libowfat_dir "${_generated_dir}/libowfat")
file(MAKE_DIRECTORY "${_generated_libowfat_dir}" "${LIBOWFAT_BUILD_PUBLIC_INCLUDE_DIR}")

function(_libowfat_rewrite_public_includes out_var content)
  set(_rewritten "${content}")
  if(NOT LIBOWFAT_INSTALL_INCLUDE_SUBDIR STREQUAL "libowfat")
    string(REPLACE "<libowfat/" "<${LIBOWFAT_INSTALL_INCLUDE_SUBDIR}/" _rewritten "${_rewritten}")
    string(REPLACE "\"libowfat/" "\"${LIBOWFAT_INSTALL_INCLUDE_SUBDIR}/" _rewritten "${_rewritten}")
  endif()
  set(${out_var} "${_rewritten}" PARENT_SCOPE)
endfunction()

function(_write_public_header name content)
  _libowfat_rewrite_public_includes(_public_content "${content}")
  file(WRITE "${LIBOWFAT_BUILD_PUBLIC_INCLUDE_DIR}/${name}" "${_public_content}")
endfunction()

function(_write_generated_header name content)
  file(WRITE "${_generated_dir}/${name}" "${content}")
  file(WRITE "${_generated_libowfat_dir}/${name}" "${content}")
  _write_public_header("${name}" "${content}")
endfunction()

function(_copy_public_header input name)
  file(READ "${input}" _header_content)
  _write_public_header("${name}" "${_header_content}")
endfunction()

function(_write_feature_header name content_if_true)
  if(${ARGV2})
    _write_generated_header("${name}" "${content_if_true}\n")
  else()
    _write_generated_header("${name}" "")
  endif()
endfunction()

_write_feature_header(haveip6.h "#define LIBC_HAS_IP6" LIBC_HAS_IP6)
_write_feature_header(havescope.h "#define LIBC_HAS_SCOPE_ID" LIBC_HAS_SCOPE_ID)
_write_feature_header(haven2i.h "#define HAVE_N2I" HAVE_N2I)
_write_feature_header(haveinline.h "#define inline" NOT_HAVE_INLINE)
if(HAVE_INLINE)
  _write_generated_header(haveinline.h "")
endif()
_write_feature_header(havekqueue.h "#define HAVE_KQUEUE" HAVE_KQUEUE)
_write_feature_header(havebsdsf.h "#define HAVE_BSDSENDFILE" HAVE_BSDSENDFILE)
_write_feature_header(havesendfile.h "#define HAVE_SENDFILE" HAVE_SENDFILE)
if(HAVE_EPOLL)
  _write_generated_header(haveepoll.h "#define HAVE_EPOLL 1\n")
else()
  _write_generated_header(haveepoll.h "")
endif()
_write_feature_header(havedevpoll.h "#define HAVE_DEVPOLL" HAVE_DEVPOLL)
_write_feature_header(havesigio.h "#define HAVE_SIGIO" HAVE_SIGIO)
_write_feature_header(haveuint128.h "#define HAVE_UINT128" HAVE_UINT128)
_write_feature_header(havepread.h "#define HAVE_PREAD" HAVE_PREAD)
_write_feature_header(haveaccept4.h "#define HAVE_ACCEPT4" HAVE_ACCEPT4)

if(HAVE_SOCKLEN_T)
  _write_generated_header(havesl.h "")
else()
  _write_generated_header(havesl.h "typedef int socklen_t;\n")
endif()

set(_havealloca_content "#include <stdlib.h>\n")
if(HAVE_ALLOCA_H)
  string(APPEND _havealloca_content "#include <alloca.h>\n")
endif()
if(HAVE_MALLOC_H)
  string(APPEND _havealloca_content "#include <malloc.h>\n")
endif()
_write_generated_header(havealloca.h "${_havealloca_content}")
if(HAVE_POLL)
  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/iopause.h2" "${_generated_dir}/iopause.h" COPYONLY)
  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/iopause.h2" "${_generated_libowfat_dir}/iopause.h" COPYONLY)
  _copy_public_header("${CMAKE_CURRENT_SOURCE_DIR}/iopause.h2" iopause.h)
else()
  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/iopause.h1" "${_generated_dir}/iopause.h" COPYONLY)
  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/iopause.h1" "${_generated_libowfat_dir}/iopause.h" COPYONLY)
  _copy_public_header("${CMAKE_CURRENT_SOURCE_DIR}/iopause.h1" iopause.h)
endif()
if(HAVE_SYS_SELECT_H)
  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/select.h2" "${_generated_dir}/select.h" COPYONLY)
  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/select.h2" "${_generated_libowfat_dir}/select.h" COPYONLY)
  _copy_public_header("${CMAKE_CURRENT_SOURCE_DIR}/select.h2" select.h)
else()
  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/select.h1" "${_generated_dir}/select.h" COPYONLY)
  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/select.h1" "${_generated_libowfat_dir}/select.h" COPYONLY)
  _copy_public_header("${CMAKE_CURRENT_SOURCE_DIR}/select.h1" select.h)
endif()

add_executable(libowfat_ent EXCLUDE_FROM_ALL "${CMAKE_CURRENT_SOURCE_DIR}/ent.c")
target_include_directories(libowfat_ent PRIVATE "${_generated_dir}" "${CMAKE_CURRENT_SOURCE_DIR}")
target_compile_definitions(libowfat_ent PRIVATE _REENTRANT)
add_custom_command(
  OUTPUT "${_generated_dir}/entities.h" "${_generated_libowfat_dir}/entities.h" "${LIBOWFAT_BUILD_PUBLIC_INCLUDE_DIR}/entities.h"
  COMMAND "${CMAKE_COMMAND}" -E copy_if_different
          "${CMAKE_CURRENT_SOURCE_DIR}/entities.json" "${_generated_dir}/entities.json"
  COMMAND libowfat_ent
  COMMAND "${CMAKE_COMMAND}" -E copy_if_different
          "${_generated_dir}/entities.h" "${_generated_libowfat_dir}/entities.h"
  COMMAND "${CMAKE_COMMAND}" -E copy_if_different
          "${_generated_dir}/entities.h" "${LIBOWFAT_BUILD_PUBLIC_INCLUDE_DIR}/entities.h"
  WORKING_DIRECTORY "${_generated_dir}"
  DEPENDS libowfat_ent "${CMAKE_CURRENT_SOURCE_DIR}/entities.json"
  COMMENT "Generating entities.h"
  VERBATIM)
add_custom_target(libowfat_entities DEPENDS "${_generated_dir}/entities.h" "${_generated_libowfat_dir}/entities.h" "${LIBOWFAT_BUILD_PUBLIC_INCLUDE_DIR}/entities.h")

set(LIBOWFAT_PUBLIC_HEADERS
  buffer.h byte.h fmt.h ip4.h ip6.h mmap.h scan.h socket.h str.h stralloc.h
  uint16.h uint32.h uint64.h open.h textcode.h tai.h taia.h dns.h iopause.h case.h
  openreadclose.h readclose.h ndelay.h array.h io.h safemult.h iob.h havealloca.h
  errmsg.h cdb.h cdb_make.h rangecheck.h iarray.h va_narg.h isset.h
  compiletimeassert.h critbit.h parse.h compiler.h clamp.h
)

foreach(_hdr IN LISTS LIBOWFAT_PUBLIC_HEADERS)
  if(EXISTS "${_generated_dir}/${_hdr}")
    configure_file("${_generated_dir}/${_hdr}" "${_generated_libowfat_dir}/${_hdr}" COPYONLY)
    _copy_public_header("${_generated_dir}/${_hdr}" "${_hdr}")
  else()
    configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${_hdr}" "${_generated_libowfat_dir}/${_hdr}" COPYONLY)
    _copy_public_header("${CMAKE_CURRENT_SOURCE_DIR}/${_hdr}" "${_hdr}")
  endif()
endforeach()

file(GLOB LIBOWFAT_SOURCES CONFIGURE_DEPENDS
  "${CMAKE_CURRENT_SOURCE_DIR}/array/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/buffer/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/byte/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/case/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/cdb/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/critbit/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/dns/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/fmt/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/io/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/mmap/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/mult/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/open/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/scan/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/socket/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/str/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/stralloc/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/tai/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/taia/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/textcode/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/uint/*.c"
  "${CMAKE_CURRENT_SOURCE_DIR}/unix/*.c"
)

add_library(libowfat_objects OBJECT ${LIBOWFAT_SOURCES})
add_dependencies(libowfat_objects libowfat_entities)
target_compile_definitions(libowfat_objects PRIVATE _REENTRANT)
target_include_directories(libowfat_objects
  PRIVATE
    "${_generated_dir}"
    "${_generated_libowfat_dir}"
    "${CMAKE_CURRENT_SOURCE_DIR}"
  PUBLIC
    "$<BUILD_INTERFACE:${_generated_dir}>"
    "$<BUILD_INTERFACE:${_generated_libowfat_dir}>"
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
    "$<BUILD_INTERFACE:${LIBOWFAT_BUILD_INCLUDE_DIR}>"
    "$<BUILD_INTERFACE:${LIBOWFAT_BUILD_PUBLIC_INCLUDE_DIR}>"
    "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
    "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${LIBOWFAT_INSTALL_INCLUDE_SUBDIR}>")
if(LIBOWFAT_BUILD_SHARED)
  set_property(TARGET libowfat_objects PROPERTY POSITION_INDEPENDENT_CODE ON)
endif()

set(_libowfat_targets)
if(LIBOWFAT_BUILD_STATIC)
  add_library(libowfat_static STATIC $<TARGET_OBJECTS:libowfat_objects>)
  list(APPEND _libowfat_targets libowfat_static)
  add_library(libowfat::static ALIAS libowfat_static)
  set_target_properties(libowfat_static PROPERTIES
    OUTPUT_NAME "${LIBOWFAT_OUTPUT_NAME}"
    VERSION "${PROJECT_VERSION}")
endif()

if(LIBOWFAT_BUILD_SHARED)
  add_library(libowfat_shared SHARED $<TARGET_OBJECTS:libowfat_objects>)
  list(APPEND _libowfat_targets libowfat_shared)
  add_library(libowfat::shared ALIAS libowfat_shared)
  set_target_properties(libowfat_shared PROPERTIES
    OUTPUT_NAME "${LIBOWFAT_OUTPUT_NAME}"
    VERSION "${PROJECT_VERSION}"
    SOVERSION "${PROJECT_VERSION_MAJOR}")
endif()

foreach(_target IN LISTS _libowfat_targets)
  target_compile_definitions(${_target} PUBLIC _REENTRANT)
  target_include_directories(${_target}
    PUBLIC
      "$<BUILD_INTERFACE:${_generated_dir}>"
      "$<BUILD_INTERFACE:${_generated_libowfat_dir}>"
      "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
      "$<BUILD_INTERFACE:${LIBOWFAT_BUILD_INCLUDE_DIR}>"
      "$<BUILD_INTERFACE:${LIBOWFAT_BUILD_PUBLIC_INCLUDE_DIR}>"
      "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
      "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${LIBOWFAT_INSTALL_INCLUDE_SUBDIR}>")
endforeach()

if(LIBOWFAT_BUILD_SHARED)
  add_library(libowfat::libowfat ALIAS libowfat_shared)
elseif(LIBOWFAT_BUILD_STATIC)
  add_library(libowfat::libowfat ALIAS libowfat_static)
endif()

function(_libowfat_disable_ndebug target)
  # A number of upstream unit tests use assert() for all checks and deliberately
  # refuse to compile when NDEBUG is set by a Release build.  Compile tests with
  # asserts enabled and without optimisation even when the library itself is built
  # as Release, matching the intent of the original test programs.
  if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
    target_compile_options(${target} PRIVATE -UNDEBUG -O0)
  elseif(MSVC)
    target_compile_options(${target} PRIVATE /UNDEBUG)
  endif()
endfunction()


if(LIBOWFAT_RUN_SELF_TESTS)
  add_executable(libowfat_smoke_t "${CMAKE_CURRENT_SOURCE_DIR}/t.c")
  target_link_libraries(libowfat_smoke_t PRIVATE libowfat::libowfat)
  _libowfat_disable_ndebug(libowfat_smoke_t)
  add_test(NAME libowfat_smoke_t COMMAND libowfat_smoke_t)
  set_tests_properties(libowfat_smoke_t PROPERTIES WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
endif()

if(LIBOWFAT_BUILD_TEST_PROGRAMS)
  file(GLOB LIBOWFAT_TEST_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/test/*.c")
  foreach(_test_src IN LISTS LIBOWFAT_TEST_SOURCES)
    get_filename_component(_test_name "${_test_src}" NAME_WE)
    string(MAKE_C_IDENTIFIER "libowfat_test_${_test_name}" _test_target)
    add_executable(${_test_target} "${_test_src}")
    target_link_libraries(${_test_target} PRIVATE libowfat::libowfat)
    _libowfat_disable_ndebug(${_test_target})
    if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
      target_compile_options(${_test_target} PRIVATE
        -Wno-error=implicit-int
        -Wno-error=incompatible-pointer-types
        -Wno-error=implicit-function-declaration)
    endif()
  endforeach()

  if(LIBOWFAT_RUN_SELF_TESTS)
    # Keep building every test/*.c program above, but only register tests that
    # are known to be stable non-interactive checks. utf8 currently aborts on
    # the upstream semantic UTF-8 surrogate assertion and is kept build-only.
    set(_libowfat_noarg_tests
      array buffer_1 buffer_tosa byte_copy cas fmt fmt_httpdate fmt_human fmt_ip6
      fmt_iso8691 fmt_long fmt_longlong fmt_strm_alloca json marshal
      mult netstring protobuf range scan scan_long stralloc_chomp textcode uint
    )
    foreach(_test_name IN LISTS _libowfat_noarg_tests)
      string(MAKE_C_IDENTIFIER "libowfat_test_${_test_name}" _test_target)
      if(TARGET ${_test_target})
        add_test(NAME ${_test_target} COMMAND ${_test_target})
        set_tests_properties(${_test_target} PROPERTIES WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
      endif()
    endforeach()
  endif()
endif()

if(LIBOWFAT_INSTALL)
  install(TARGETS ${_libowfat_targets}
    EXPORT libowfat-targets
    ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
    LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
    RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
  install(DIRECTORY "${LIBOWFAT_BUILD_PUBLIC_INCLUDE_DIR}/"
    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${LIBOWFAT_INSTALL_INCLUDE_SUBDIR}"
    FILES_MATCHING PATTERN "*.h")
  install(EXPORT libowfat-targets
    NAMESPACE libowfat::
    DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/libowfat")
  configure_package_config_file(
    "${CMAKE_CURRENT_SOURCE_DIR}/cmake/libowfat-config.cmake.in"
    "${CMAKE_CURRENT_BINARY_DIR}/libowfat-config.cmake"
    INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/libowfat")
  write_basic_package_version_file(
    "${CMAKE_CURRENT_BINARY_DIR}/libowfat-config-version.cmake"
    VERSION "${PROJECT_VERSION}"
    COMPATIBILITY SameMajorVersion)
  install(FILES
    "${CMAKE_CURRENT_BINARY_DIR}/libowfat-config.cmake"
    "${CMAKE_CURRENT_BINARY_DIR}/libowfat-config-version.cmake"
    DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/libowfat")
endif()

message(STATUS "libowfat ${PROJECT_VERSION}: building lib${LIBOWFAT_OUTPUT_NAME} static=${LIBOWFAT_BUILD_STATIC} shared=${LIBOWFAT_BUILD_SHARED}")
