message (STATUS "Running with CMake ${CMAKE_VERSION}")
cmake_minimum_required (VERSION 3.15)

project (agentx_pp VERSION 3.0.1 LANGUAGES CXX)
enable_language (CXX)

include (CheckIncludeFiles)
include (CheckFunctionExists)
include (CheckSymbolExists)
include (CheckLibraryExists)
include (CheckStructHasMember)
include (CheckTypeSize)

set (CMAKE_CXX_STANDARD 14)

list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules)

# Provides CMAKE_INSTALL_LIBDIR / BINDIR / INCLUDEDIR
include (GNUInstallDirs)

option (OPTION_NAMESPACE  "Use Namespaces" ON)
option (OPTION_PROXY  "Enable Proxy" ON)
option (OPTION_PROXY_FORWARDER  "Enable Proxy Forwarder" ON)
option (OPTION_MASTER  "Enable support for master agent" ON)
option (OPTION_SUBAGENT  "Enable support for subagent" ON)
option (OPTION_AGENPRO_MASTER  "build with AgenPro master templates (usually disabled)" OFF)
option (OPTION_AGENPRO_SUBAGENT  "build with AgenPro subagent templates (usually disabled)" OFF)
option (OPTION_EXAMPLES  "build examples" ON)

set (VERSION ${PROJECT_VERSION})
set (AGENTX_PP_MAJOR_VERSION ${PROJECT_VERSION_MAJOR})
set (AGENTX_PP_MINOR_VERSION ${PROJECT_VERSION_MINOR})
set (AGENTX_PP_MICRO_VERSION ${PROJECT_VERSION_PATCH})

message (STATUS "agentx++ Version ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")

check_include_files (sys/types.h CNF_HAVE_SYS_TYPES_H)
check_include_files (sys/stat.h CNF_HAVE_SYS_STAT_H)
check_include_files ("cctype;cerrno;climits;csignal;cstddef;cstdio;cstdlib;cstring;ctime" CNF_STDCXX_98_HEADERS)
check_include_files ("iostream" CNF_HAVE_IOSTREAM)
check_include_files ("stdlib.h;stddef.h" CNF_STDC_HEADERS)
check_include_files (stdlib.h CNF_HAVE_STDLIB_H)
check_include_files (string.h CNF_HAVE_STRING_H)
check_include_files (memory.h CNF_HAVE_MEMORY_H)
check_include_files (strings.h CNF_HAVE_STRINGS_H)
check_include_files (inttypes.h CNF_HAVE_INTTYPES_H)
check_include_files (ctype.h CNF_HAVE_CTYPE_H)
check_include_files (ctype.h HAVE_CTYPE_H)
check_include_files (signal.h CNF_HAVE_SIGNAL_H)
check_include_files (errno.h CNF_HAVE_ERRNO_H)
check_include_files (time.h CNF_HAVE_TIME_H)
check_include_files (unistd.h CNF_HAVE_UNISTD_H)
check_include_files (sys/unistd.h CNF_HAVE_SYS_UNISTD_H)
check_include_files (stdint.h CNF_HAVE_STDINT_H)
check_include_files (sys/time.h CNF_HAVE_SYS_TIME_H)
check_include_files (sys/param.h CNF_HAVE_SYS_PARAM_H)
check_include_files (sys/timeb.h CNF_HAVE_SYS_TIMEB_H)
check_include_files (winsock2.h CNF_HAVE_WINSOCK2_H)
check_include_files (ws2tcpip.h CNF_HAVE_WS2TCPIP_H)
check_include_files (wspiapi.h CNF_HAVE_WSPIAPI_H)
check_include_files (winsock.h CNF_HAVE_WINSOCK_H)
check_include_files (netdb.h CNF_HAVE_NETDB_H)
check_include_files (sys/socket.h CNF_HAVE_SYS_SOCKET_H)
check_include_files (arpa/inet.h CNF_HAVE_ARPA_INET_H)
check_include_files (arpa/inet.h HAVE_ARPA_INET_H)
check_include_files (netinet/in.h CNF_HAVE_NETINET_IN_H)
check_include_files (poll.h CNF_HAVE_POLL_H)
check_include_files (sys/select.h CNF_HAVE_SYS_SELECT_H)
check_include_files (io.h CNF_HAVE_IO_H)
check_include_files (process.h CNF_HAVE_PROCESS_H)
check_include_files (pthread.h CNF_HAVE_PTHREAD_H)
check_function_exists ("strcasecmp" CNF_HAVE_STRCASECMP)
check_function_exists ("stricmp" CNF_HAVE_STRICMP)
check_function_exists ("getpid" CNF_HAVE_GETPID)
check_function_exists ("_getpid" CNF_HAVE__GETPID)

check_function_exists ("clock_gettime" HAVE_CLOCK_GETTIME)
check_function_exists ("getaddrinfo" HAVE_GETADDRINFO)
check_function_exists ("gethostbyaddr" HAVE_GETHOSTBYADDR)
check_function_exists ("gethostbyaddr_r" HAVE_GETHOSTBYADDR_R)
check_function_exists ("gethostbyname" HAVE_GETHOSTBYNAME)
check_function_exists ("gethostbyname2" HAVE_GETHOSTBYNAME2)
check_function_exists ("gethostbyname_r" HAVE_GETHOSTBYNAME_R)
check_function_exists ("gethostname" HAVE_GETHOSTNAME)
check_function_exists ("getpid" HAVE_GETPID)
check_function_exists ("gettimeofday" HAVE_GETTIMEOFDAY)
check_function_exists ("htonl" HAVE_HTONL)
check_function_exists ("inet_aton" HAVE_INET_ATON)
check_function_exists ("inet_ntoa" HAVE_INET_NTOA)
check_function_exists ("inet_ntop" HAVE_INET_NTOP)
check_function_exists ("inet_pton" HAVE_INET_PTON)
check_include_files (inttypes.h HAVE_INTTYPES_H)
check_include_files (io.h HAVE_IO_H)
check_function_exists ("isdigit" HAVE_ISDIGIT)
check_include_files (limits.h HAVE_LIMITS_H)
check_function_exists ("localtime_r" HAVE_LOCALTIME_R)
check_function_exists ("malloc" HAVE_MALLOC)
check_include_files (memory.h HAVE_MEMORY_H)
check_function_exists ("memset" HAVE_MEMSET)
check_include_files (netdb.h HAVE_NETDB_H)
check_include_files (netinet/in.h HAVE_NETINET_IN_H)
check_function_exists ("poll" HAVE_POLL)
check_include_files (poll.h HAVE_POLL_H)
check_include_files (process.h HAVE_PROCESS_H)
check_include_files (pthread.h HAVE_PTHREAD) # :-(
check_function_exists ("realloc" HAVE_REALLOC)
check_function_exists ("select" HAVE_SELECT)
check_include_files (signal.h HAVE_SIGNAL_H)
check_function_exists ("socket" HAVE_SOCKET)
check_include_files (stdint.h HAVE_STDINT_H)
check_include_files (stdio.h HAVE_STDIO_H)
check_include_files (stdlib.h HAVE_STDLIB_H)
check_function_exists ("strcasecmp" HAVE_STRCASECMP)
check_function_exists ("strchr" HAVE_STRCHR)
check_function_exists ("strerror" HAVE_STRERROR)
check_function_exists ("stricmp" HAVE_STRICMP)
check_include_files (strings.h HAVE_STRINGS_H)
check_include_files (string.h HAVE_STRING_H)
check_function_exists ("strstr" HAVE_STRSTR)
check_include_files (sys/param.h HAVE_SYS_PARAM_H)
check_include_files (sys/select.h HAVE_SYS_SELECT_H)
check_include_files (sys/socket.h HAVE_SYS_SOCKET_H)
check_include_files (sys/stat.h HAVE_SYS_STAT_H)
check_include_files (sys/timeb.h HAVE_SYS_TIMEB_H)
check_include_files (sys/time.h HAVE_SYS_TIME_H)
check_include_files (sys/types.h HAVE_SYS_TYPES_H)
check_include_files (sys/unistd.h HAVE_SYS_UNISTD_H)
check_include_files (time.h HAVE_TIME_H)
check_include_files (unistd.h HAVE_UNISTD_H)
check_include_files (winsock2.h HAVE_WINSOCK2_H)
check_include_files (winsock.h HAVE_WINSOCK_H)
check_include_files (ws2tcpip.h HAVE_WS2TCPIP_H)
check_include_files (wspiapi.h HAVE_WSPIAPI_H)
check_function_exists ("_getpid" HAVE__GETPID)

check_type_size ("pthread_t" SIZEOF_PTHREAD_T LANGUAGE CXX)
check_type_size ("long" SIZEOF_LONG LANGUAGE CXX)
check_type_size ("int" SIZEOF_INT LANGUAGE CXX)

if (SIZEOF_PTHREAD_T EQUAL SIZEOF_LONG)
  set (AGENTPP_OPAQUE_PTHREAD_T "long")
elseif (SIZEOF_PTHREAD_T EQUAL SIZEOF_INT)
  set (AGENTPP_OPAQUE_PTHREAD_T "long")
else ()
  message (FATAL_ERROR "Can't map pthread_t to int or long")
endif ()
message (STATUS "Found AGENTPP_OPAQUE_PTHREAD_T: ${AGENTPP_OPAQUE_PTHREAD_T}")


set (SOCKADDR_HEADERS)
if (CNF_HAVE_WINSOCK2_H)
   set (SOCKADDR_HEADERS ${SOCKADDR_HEADERS} winsock2.h)
endif ()
if (CNF_HAVE_WS2TCPIP_H)
   set (SOCKADDR_HEADERS ${SOCKADDR_HEADERS} ws2tcpip.h)
endif ()
if (CNF_HAVE_WINSOCK_H)
   set (SOCKADDR_HEADERS ${SOCKADDR_HEADERS} winsock.h)
endif ()
if (CNF_HAVE_NETDB_H)
   set (SOCKADDR_HEADERS ${SOCKADDR_HEADERS} netdb.h)
endif ()
if (CNF_HAVE_SYS_SOCKET_H)
   set (SOCKADDR_HEADERS ${SOCKADDR_HEADERS} sys/socket.h)
endif ()
if (CNF_HAVE_ARPA_INET_H)
   set (SOCKADDR_HEADERS ${SOCKADDR_HEADERS} arpa/inet.h)
endif ()
if (CNF_HAVE_NETINET_IN_H)
   set (SOCKADDR_HEADERS ${SOCKADDR_HEADERS} netinet/in.h)
endif ()

check_struct_has_member ("struct sockaddr_storage"
            ss_family "${SOCKADDR_HEADERS}"
            CNF_HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY LANGUAGE CXX)

check_struct_has_member ("struct sockaddr_storage"
            __ss_family "${SOCKADDR_HEADERS}"
            CNF_HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY LANGUAGE CXX)

check_type_size ("bool" SIZEOF_BOOL LANGUAGE CXX)

if (SIZEOF_BOOL)
  message (STATUS "HAVE_BOOL")
  set (NEED_DEFINE_BOOL 0)
else ()
  message (STATUS "NO HAVE_BOOL")
  set (NEED_DEFINE_BOOL 1)
endif ()

if (OPTION_PROXY)
  set (WITH_PROXY 1)
else ()
  set (WITH_PROXY 0)
endif ()

if (OPTION_PROXY_FORWARDER)
  set (WITH_PROXY_FORWARDER 1)
else ()
  set (WITH_PROXY_FORWARDER 0)
endif ()

if (OPTION_NAMESPACE)
  set (WITH_NAMESPACE 1)
else ()
  set (WITH_NAMESPACE 0)
endif ()

if (OPTION_MASTER)
  set (WITH_MASTER 1)
else ()
  set (WITH_MASTER 0)
endif ()

if (OPTION_SUBAGENT)
  set (WITH_SUBAGENT 1)
else ()
  set (WITH_SUBAGENT 0)
endif ()


if (CNF_STDC_HEADERS)
  set (STDC_HEADERS 1)
else ()
  set (STDC_HEADERS 0)
endif ()

if (CNF_STDCXX_98_HEADERS)
  set (STDCXX_98_HEADERS 1)
else ()
  set (STDCXX_98_HEADERS 0)
endif ()

check_include_files (malloc.h HAVE_MALLOC_H)
check_include_files ("sys/param.h;sys/mount.h" HAVE_SYS_MOUNT_H)
check_function_exists ("strcasecmp" HAVE_STRCASECMP)

set (CMAKE_REQUIRED_LINK_OPTIONS "-lpthread")
check_function_exists (pthread_mutex_timedlock HAVE_PTHREAD_MUTEX_TIMEDLOCK)

# agentx++ cannot be built without thread support: the AgentX++ master agent
# and subagent are built on AGENT++'s Mib, RequestList and ThreadPool, which
# have no single threaded fallback. There is deliberately no OPTION_THREADS,
# and Threads is REQUIRED here so a missing thread library fails at configure
# time instead of in the preprocessor (see include/agentx_pp/agentx++.h.in).
set (CMAKE_THREAD_PREFER_PTHREAD 1)
set (THREADS_PREFER_PTHREAD_FLAG ON)
# Always go through FindThreads so that the imported target Threads::Threads
# exists on every platform (including Apple) and can be exported by name.
find_package (Threads REQUIRED)


# agent++ 5.0.1 and later install a CMake package that provides the imported
# targets agent++::agent++ / agent++::agent++_static carrying their include
# directories and their snmp++ (and thus OpenSSL) dependency as usage
# requirements. Prefer it, and fall back to the bundled Findagent_pp and
# Findsnmp_pp modules for an autoconf install, an older agent++, or plain
# agent++/snmp++ source trees.
#
# Either way agentx++ links an IMPORTED target: only imported target names may
# appear in the exported link interface - an absolute host path such as
# /usr/local/lib/libagent++.so would be written verbatim into the installed
# agentx++Targets.cmake and make the package non-relocatable.
# The version is required, not just preferred: agentx++ 3.x needs agent++ 5.x
# anyway, and only the package of agent++ 5.0.1 and later carries the include
# directories - the one of agent++ 5.0.0 exports none. Without the version such
# a package is accepted here and then contributes no include directory at all,
# so the build fails much later with "agent_pp/agent++.h file not found" rather
# than at configure time. A rejected version does not create the imported
# targets, which is what lets the fallback below take over.
find_package (agent++ 5.0.1 CONFIG QUIET HINTS ${AGENT_PP_ROOT_DIR})
if (agent++_FOUND)
  # A package built from a modified agent++ tree can still export a target
  # without include directories. Diagnose that here, where the cause is still
  # visible - it cannot be repaired by falling back to Findagent_pp, because the
  # imported target already exists and agent++Dependency.cmake would skip it.
  get_target_property (AGENT_PP_INTERFACE_INCLUDE_DIRS
                       agent++::agent++ INTERFACE_INCLUDE_DIRECTORIES)
  if (NOT AGENT_PP_INTERFACE_INCLUDE_DIRS)
    message (FATAL_ERROR
      "The agent++ CMake package in ${agent++_DIR} exports no include "
      "directories, so <agent_pp/*.h> would not be found. Reinstall agent++, or "
      "point agentx++ at another agent++ with -D agent++_DIR=... , or bypass the "
      "package with -D CMAKE_DISABLE_FIND_PACKAGE_agent++=ON "
      "-D AGENT_PP_ROOT_DIR=/path/to/agent++")
  endif ()
  unset (AGENT_PP_INTERFACE_INCLUDE_DIRS)

  # agent++Config.cmake pulls snmp++ in with an unversioned find_dependency, so
  # an outdated snmp++ package can arrive on this path even though the version
  # pin in cmake/agent++Dependency.cmake would have rejected it - that pin is
  # not reached here. The imported target cannot be repaired from the outside
  # either, so snmp++::snmp++ gets the same diagnosis.
  if (TARGET snmp++::snmp++)
    get_target_property (SNMP_PP_INTERFACE_INCLUDE_DIRS
                         snmp++::snmp++ INTERFACE_INCLUDE_DIRECTORIES)
    if (NOT SNMP_PP_INTERFACE_INCLUDE_DIRS)
      message (FATAL_ERROR
        "The agent++ CMake package in ${agent++_DIR} depends on the snmp++ "
        "CMake package in ${snmp++_DIR}, which exports no include directories, "
        "so <snmp_pp/*.h> and the OpenSSL headers they include would not be "
        "found. Install snmp++ 4.0.1 or later, remove a stale "
        "<prefix>/lib/cmake/snmp++ left behind by an earlier snmp++, select "
        "another one with -D snmp++_DIR=... , or bypass both packages with "
        "-D CMAKE_DISABLE_FIND_PACKAGE_agent++=ON "
        "-D AGENT_PP_ROOT_DIR=/path/to/agent++")
    endif ()
    unset (SNMP_PP_INTERFACE_INCLUDE_DIRS)
  endif ()

  set (AGENTX_PP_AGENT_PP_HAS_CMAKE_PACKAGE 1)
  message (STATUS "Found agent++ CMake package ${agent++_VERSION} in ${agent++_DIR}")
else ()
  set (AGENTX_PP_AGENT_PP_HAS_CMAKE_PACKAGE 0)
  include (${PROJECT_SOURCE_DIR}/cmake/agent++Dependency.cmake)
endif ()


configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake_config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/config.h)
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake_libagentx.h.in ${CMAKE_CURRENT_SOURCE_DIR}/libagentx.h)
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/include/agentx_pp/agentx++.h.in ${CMAKE_CURRENT_SOURCE_DIR}/include/agentx_pp/agentx++.h)

set (CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -DHAVE_CONFIG_H")

if (MSVC)
    # warning level 4 (all warnings as errors: /WX)
    add_compile_options (/W4 /WX)
else ()
    # lots of warnings (-Wextra -pedantic)
    add_compile_options (-Wall )
endif ()

set (MY_HEADER_FILES
  include/agentx_pp/agentpp_agentx_mib.h
  include/agentx_pp/agentx_def.h
  include/agentx_pp/agentx.h
  include/agentx_pp/agentx++.h
  include/agentx_pp/agentx_index.h
  include/agentx_pp/agentx_master.h
  include/agentx_pp/agentx_mib.h
  include/agentx_pp/agentx_node.h
  include/agentx_pp/agentx_pdu.h
  include/agentx_pp/agentx_peer.h
  include/agentx_pp/agentx_queue.h
  include/agentx_pp/agentx_reg.h
  include/agentx_pp/agentx_request.h
  include/agentx_pp/agentx_session.h
  include/agentx_pp/agentx_subagent.h
  include/agentx_pp/agentx_threads.h
)

set (MY_HEADER_LIB_FILES
  libagentx.h
)

set (MY_SRC_FILES
  src/agentpp_agentx_mib.cpp
  src/agentx.cpp
  src/agentx_index.cpp
  src/agentx_master.cpp
  src/agentx_mib.cpp
  src/agentx_node.cpp
  src/agentx_pdu.cpp
  src/agentx_peer.cpp
  src/agentx_queue.cpp
  src/agentx_reg.cpp
  src/agentx_request.cpp
  src/agentx_session.cpp
  src/agentx_subagent.cpp
)

add_library (agentx++ SHARED
  ${MY_HEADER_FILES}
  ${MY_HEADER_LIB_FILES}
  config.h
  ${MY_SRC_FILES}
)

set_property (TARGET agentx++ PROPERTY POSITION_INDEPENDENT_CODE 1)
# Set .so version as autoconf does: agentx++ version 1.2.3 will go to .so.12.0.3!
set_target_properties (agentx++ PROPERTIES VERSION ${PROJECT_VERSION_MAJOR}${PROJECT_VERSION_MINOR}.0.${PROJECT_VERSION_PATCH} SOVERSION ${PROJECT_VERSION_MAJOR}${PROJECT_VERSION_MINOR})

add_library (agentx++_static STATIC
  ${MY_HEADER_FILES}
  ${MY_SRC_FILES}
)

# ---------------------------------------------------------------------------
# Usage requirements, shared by the shared and the static library.
#
# Note the asymmetry between BUILD_INTERFACE and INSTALL_INTERFACE: in the
# source tree <libagentx.h> lives in the project root (generated by
# configure_file above) while "agentx_pp/*.h" live under include/, so two build
# directories are needed. Both land under <prefix>/include on install, so a
# single install directory covers them.
# ---------------------------------------------------------------------------
foreach (_agentx_pp_target agentx++ agentx++_static)

  target_include_directories (${_agentx_pp_target}
    PUBLIC
      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
      $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
  )

  # HAVE_CONFIG_H selects the (non-installed) config.h in libagentx.h and must
  # NOT leak into the interface.
  target_compile_definitions (${_agentx_pp_target} PRIVATE HAVE_CONFIG_H)

  # PUBLIC: the installed agentx_pp headers include <agent_pp/*.h> and
  # <snmp_pp/*.h>, and <agentx_pp/agentx_threads.h> builds on AGENT++'s
  # threads, so consumers need the agent++ and thread usage requirements as
  # well. snmp++ and OpenSSL arrive transitively through agent++.
  target_link_libraries (${_agentx_pp_target} PUBLIC agent++::agent++ Threads::Threads)

  # CMAKE_CXX_STANDARD is directory scope and is not exported, so state the
  # requirement on the targets instead.
  target_compile_features (${_agentx_pp_target} PUBLIC cxx_std_14)

endforeach ()
unset (_agentx_pp_target)

if (WIN32)
  set_target_properties (agentx++ PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif ()

macro (subdirlist result curdir)
  #message (INFO " # Get subdirectories for ${curdir} ")
  file (GLOB children RELATIVE ${curdir} ${curdir}/*)
  set (dirlist "")
  foreach (child ${children})
    #message (INFO " ## check ${child}")
    if (IS_DIRECTORY ${curdir}/${child})
      #message (INFO " ### Add ${child}")
      list (APPEND dirlist ${child})
    endif ()
  endforeach ()
  set (${result} ${dirlist})
endmacro ()

set (EXE_BASE_DIRS )

if (OPTION_AGENPRO_AGENT OR OPTION_AGENPRO_SUBAGENT)
  list (APPEND EXE_BASE_DIRS agenpro)
endif ()

if (OPTION_EXAMPLES)
  list (APPEND EXE_BASE_DIRS examples)
endif ()

foreach (EXE_BASE_DIR ${EXE_BASE_DIRS})
  message (INFO " # Process dir ${EXE_BASE_DIR}")
  subdirlist (EXE_SUB_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/${EXE_BASE_DIR})
  foreach (EXE_SUB_DIR ${EXE_SUB_DIRS})
    message (INFO " ## Process executable in ${EXE_BASE_DIR}/${EXE_SUB_DIR}")

    if (OPTION_PROXY_FORWARDER AND
       (${EXE_SUB_DIR} STREQUAL "proxy") AND
       (${EXE_BASE_DIR} STREQUAL "examples"))
      message (INFO " ## Do not build ${EXE_BASE_DIR}/${EXE_SUB_DIR}, as OPTION_PROXY_FORWARDER is enabled")
      continue ()
    endif ()

    if (${EXE_BASE_DIR} STREQUAL "agenpro")
      if ((${EXE_SUB_DIR} STREQUAL "master") AND
         NOT OPTION_AGENPRO_AGENT)
        message (INFO " ## Do not build ${EXE_BASE_DIR}/${EXE_SUB_DIR}, as OPTION_AGENPRO_AGENT is disabled")
        continue ()
      endif ()
      if ((${EXE_SUB_DIR} STREQUAL "subagent") AND
         NOT OPTION_AGENPRO_SUBAGENT)
        message (INFO " ## Do not build ${EXE_BASE_DIR}/${EXE_SUB_DIR}, OPTION_AGENPRO_SUBAGENT is disabled")
        continue ()
      endif ()
    endif ()

    if (${EXE_BASE_DIR} STREQUAL "examples")
      if (${EXE_SUB_DIR} STREQUAL "subagent")
        message (INFO " ## examples/subagent needs special handling...")
        continue ()
      endif ()
    endif ()

    file (GLOB EXE_SRC
      "${CMAKE_CURRENT_SOURCE_DIR}/${EXE_BASE_DIR}/${EXE_SUB_DIR}/include/*.h"
      "${CMAKE_CURRENT_SOURCE_DIR}/${EXE_BASE_DIR}/${EXE_SUB_DIR}/src/*.cpp"
      )

    add_executable (${EXE_BASE_DIR}${EXE_SUB_DIR} ${EXE_SRC})
    # agentx++ carries its include directories, HAVE_CONFIG_H is private to the
    # library, so the examples need their own definition. agent++::agent++,
    # snmp++::snmp++ and Threads::Threads arrive transitively as PUBLIC
    # dependencies of agentx++.
    target_compile_definitions (${EXE_BASE_DIR}${EXE_SUB_DIR} PRIVATE HAVE_CONFIG_H)
    target_link_libraries (${EXE_BASE_DIR}${EXE_SUB_DIR} agentx++)
    target_include_directories (${EXE_BASE_DIR}${EXE_SUB_DIR} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/${EXE_BASE_DIR}/${EXE_SUB_DIR}/include/")
    set_target_properties (${EXE_BASE_DIR}${EXE_SUB_DIR} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${EXE_BASE_DIR}/${EXE_SUB_DIR}/src")
    set_target_properties (${EXE_BASE_DIR}${EXE_SUB_DIR} PROPERTIES OUTPUT_NAME "${EXE_SUB_DIR}")

  endforeach ()
endforeach ()


if (OPTION_EXAMPLES)
  message (INFO " ## Process executable examples/subagent")

  set (EXE_INC 
      ${CMAKE_CURRENT_SOURCE_DIR}/examples/subagent/include/agentpp_notifytest_mib.h
      ${CMAKE_CURRENT_SOURCE_DIR}/examples/subagent/include/agentpp_test_mib.h
      ${CMAKE_CURRENT_SOURCE_DIR}/examples/subagent/include/atm_mib.h
      ${CMAKE_CURRENT_SOURCE_DIR}/examples/subagent/include/if_mib.h
    )

  set (EXE_SRC
      ${CMAKE_CURRENT_SOURCE_DIR}/examples/subagent/src/atm_mib.cpp
      ${CMAKE_CURRENT_SOURCE_DIR}/examples/subagent/src/if_mib.cpp
      ${CMAKE_CURRENT_SOURCE_DIR}/examples/subagent/src/subagent.cpp
    ) 

  set (EXE_SRC_2
      ${CMAKE_CURRENT_SOURCE_DIR}/examples/subagent/src/agentpp_notifytest_mib.cpp
      ${CMAKE_CURRENT_SOURCE_DIR}/examples/subagent/src/agentpp_test_mib.cpp
      ${CMAKE_CURRENT_SOURCE_DIR}/examples/subagent/src/atm_mib.cpp
      ${CMAKE_CURRENT_SOURCE_DIR}/examples/subagent/src/if_mib.cpp
      ${CMAKE_CURRENT_SOURCE_DIR}/examples/subagent/src/subagent2.cpp
    ) 

  add_executable (subagent ${EXE_SRC} ${EXE_INC})
  target_compile_definitions (subagent PRIVATE HAVE_CONFIG_H)
  target_link_libraries (subagent agentx++)
  target_include_directories (subagent PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/examples/subagent/include/")
  set_target_properties (subagent PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/examples/subagent/src")

  add_executable (subagent2 ${EXE_SRC_2} ${EXE_INC})
  target_compile_definitions (subagent2 PRIVATE HAVE_CONFIG_H)
  target_link_libraries (subagent2 agentx++)
  target_include_directories (subagent2 PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/examples/subagent/include/")
  set_target_properties (subagent2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/examples/subagent/src")

endif ()


# ---------------------------------------------------------------------------
# Installation and package export
# ---------------------------------------------------------------------------
include (CMakePackageConfigHelpers)

set (AGENTX_PP_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/agentx++"
     CACHE STRING "Installation directory for the agentx++ CMake package files")

install (TARGETS agentx++ agentx++_static
  EXPORT  agentx++Targets
  RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"     # .dll on Windows
  LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"     # .so / .dylib
  ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"     # .a / .lib
)

install (FILES ${MY_HEADER_LIB_FILES} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
install (FILES ${MY_HEADER_FILES}     DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/agentx_pp")

install (EXPORT agentx++Targets
  FILE        agentx++Targets.cmake
  NAMESPACE   agentx++::
  DESTINATION "${AGENTX_PP_INSTALL_CMAKEDIR}"
)

# Build-tree export, so a superproject can find_package(agentx++) against an
# uninstalled build directory.
export (EXPORT agentx++Targets
  FILE      "${CMAKE_CURRENT_BINARY_DIR}/agentx++Targets.cmake"
  NAMESPACE agentx++::
)

configure_package_config_file (
  "${PROJECT_SOURCE_DIR}/cmake/agentx++Config.cmake.in"
  "${CMAKE_CURRENT_BINARY_DIR}/agentx++Config.cmake"
  INSTALL_DESTINATION "${AGENTX_PP_INSTALL_CMAKEDIR}"
)

write_basic_package_version_file (
  "${CMAKE_CURRENT_BINARY_DIR}/agentx++ConfigVersion.cmake"
  VERSION       ${PROJECT_VERSION}
  COMPATIBILITY SameMajorVersion
)

install (FILES
  "${CMAKE_CURRENT_BINARY_DIR}/agentx++Config.cmake"
  "${CMAKE_CURRENT_BINARY_DIR}/agentx++ConfigVersion.cmake"
  DESTINATION "${AGENTX_PP_INSTALL_CMAKEDIR}"
)

if (NOT AGENTX_PP_AGENT_PP_HAS_CMAKE_PACKAGE)
  # The installed agentx++Config.cmake has to recreate the agent++::agent++
  # imported target the same way this build did, so it needs the helpers and
  # the Find modules alongside it.
  install (FILES
    "${PROJECT_SOURCE_DIR}/cmake/agent++Dependency.cmake"
    "${PROJECT_SOURCE_DIR}/cmake/snmp++Dependency.cmake"
    DESTINATION "${AGENTX_PP_INSTALL_CMAKEDIR}")
  install (FILES
    "${PROJECT_SOURCE_DIR}/cmake/modules/Findagent_pp.cmake"
    "${PROJECT_SOURCE_DIR}/cmake/modules/Findsnmp_pp.cmake"
    DESTINATION "${AGENTX_PP_INSTALL_CMAKEDIR}/modules")
endif ()

