cmake_minimum_required( VERSION 3.10 )

# CMake 3.25 added a predefined LINUX variable set to 1. With the policy that
# CMake 3.0 implied, a quoted "LINUX" in an if() was still dereferenced, so the
# platform detection below compared LINUX against 1 and silently decided the
# system was unknown. Raising the minimum is what makes CMP0054 NEW and the
# comparison mean what it reads. Set explicitly so that lowering the minimum
# again cannot bring the bug back.
cmake_policy( SET CMP0054 NEW )
project( TOL )

#-----------------------------------------------------------------------------
#
# To fix compilation problem: relocation R_X86_64_32 against `a local symbol' can not be
# used when making a shared object; recompile with -fPIC
# See http://www.cmake.org/pipermail/cmake/2007-May/014350.html
#
if( CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT WIN32 )
  set( NEED_FPIC "-fPIC" )
else( CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT WIN32 )
  unset( NEED_FPIC )
endif( CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT WIN32 )

if( TOL_EXTERNAL_DEVEL_DIR )
  if( CMAKE_BUILD_TYPE MATCHES Debug )
    set( TOL_EXTERNAL_PREFIX "${TOL_EXTERNAL_DEVEL_DIR}/debug" )
  else( CMAKE_BUILD_TYPE MATCHES DEBUG )
    set( TOL_EXTERNAL_PREFIX "${TOL_EXTERNAL_DEVEL_DIR}/release" )
  endif( CMAKE_BUILD_TYPE MATCHES Debug )
  set( CMAKE_PREFIX_PATH "${TOL_EXTERNAL_PREFIX}" ${CMAKE_PREFIX_PATH} )
  set( GSL_INCLUDE_SEARCH "${TOL_EXTERNAL_PREFIX}/include" )
  set( GSL_LIBS_SEARCH "${TOL_EXTERNAL_PREFIX}/lib" )
  set( CBLAS_DIR "${TOL_EXTERNAL_PREFIX}" )
  set( CLAPACK_DIR "${TOL_EXTERNAL_PREFIX}" )
  set( ZIPARCHIVE_AS_STATIC 0 )
  set( CINT_ROOT_DIR "${TOL_EXTERNAL_PREFIX}/include/cint" )
  set( PKG_FFTW_LIBRARY_DIRS "${TOL_EXTERNAL_PREFIX}/lib" CACHE INTERNAL "" )
  set( PKG_FFTW_INCLUDE_DIRS "${TOL_EXTERNAL_PREFIX}/include" CACHE INTERNAL "" )
  set( CMAKE_LIBRARY_PATH "${TOL_EXTERNAL_PREFIX}/lib" )
  find_library( FFTW_LIB
    NAMES "fftw3"
    PATHS ${PKG_FFTW_LIBRARY_DIRS}
    )
  #message( "aaaaaa FFTW_LIB ${FFTW_LIB}" )
  #-DFFTW_ROOT:PATH=c:/MinGW/msys/1.0/local -DFFTW_USE_STATIC_LIBS=1 
  set( CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/Runtime_Base" )
endif( )

set( TRACE_COMPONENTS_FOUND 1 )

message( "CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}" )

set( CMAKE_MODULE_PATH
  "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules"
  ${CMAKE_MODULE_PATH} )
include( MSysMacros )
include( GetPrerequisites )

if( MSYS )
  msys_convert_path( msys_usrlocal "/usr/local" )
  set( CMAKE_PREFIX_PATH "${msys_usrlocal}" ${CMAKE_PREFIX_PATH} )
  include_directories( "${msys_usrlocal}/include" )
endif( MSYS )

# El byte del SDK de Windows contra el std::byte de C++17.
#
# rpcndr.h --al que se llega por windows.h, winscard.h y wtypes.h-- declara
# "typedef unsigned char byte;" en el ambito GLOBAL, y <cstddef> declara
# "enum class std::byte". Como tol_bcommon.h hace "using namespace std" y lo
# incluye todo, los dos nombres quedan visibles a la vez y cada mencion de byte
# dentro de las cabeceras del SDK es ambigua. En 2017 no pasaba porque std::byte
# llego con C++17.
#
# Se renombra el del SDK para toda la compilacion. Es un mazo, pero es lo unico
# fiable: la cadena de inclusiones que acaba en rpcndr.h pasa por winscard.h,
# ole2.h, unknwn.h, objidl.h y urlmon.h entre otros, y encauzar los sitios de uno
# en uno dejaba apareciendo errores nuevos en cada modulo. Es seguro porque TOL no
# usa el identificador byte en ninguna parte --comprobado-- ni usa std::byte.
#
# WIN32_LEAN_AND_MEAN no sirve: el windows.h de mingw-w64 incluye winscard.h de
# todas formas, y ademas deja fuera shellapi.h, que hace falta para ShellExecute.
if( MINGW )
  set( CMAKE_CXX_STANDARD 14 )
  set( CMAKE_CXX_STANDARD_REQUIRED ON )
  # Se exporta todo de libtol.dll. Hace falta porque los cuerpos de varios
  # miembros de BGraContensBase<T> viven en .cpp que no ven la linea
  # "template class TOL_API BGraContensBase<T>;", asi que su instanciacion alli
  # es IMPLICITA y el atributo de exportacion no se aplica: el simbolo queda
  # definido dentro de la DLL pero fuera de su tabla de exportacion, y la
  # biblioteca de importacion no lo lleva. Con dllexport presente, el enlazador de
  # mingw desactiva la exportacion automatica, asi que hay que pedirla.
  #
  # Se vio al enlazar toltcl: 36 referencias sin definir de BGraContensBase<BCode>,
  # <BDat>, <BNameBlock> y otros, todas presentes en la DLL como codigo pero
  # ausentes de libtol.dll.a.
  set( CMAKE_SHARED_LINKER_FLAGS
       "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--export-all-symbols" )
endif( MINGW )

if ( MINGW )
  #add_definitions( "-Wno-attributes" )
  add_definitions( "-Wall" )
endif( MINGW )
if( WIN32 )
  add_definitions( -DTOLDLL_EXPORTS )
endif( WIN32 )

if( CHECK_SVN )
  find_package(Subversion)
endif( CHECK_SVN )

if( SUBVERSION_FOUND )
  Subversion_WC_INFO( ${PROJECT_SOURCE_DIR} Project )
  message( "Current revision is ${Project_WC_REVISION}" )
  Subversion_WC_LOG( ${PROJECT_SOURCE_DIR} Project )
  message( "Last changed log is ${Project_LAST_CHANGED_LOG}" )
else( )
  set( Project_WC_REVISION -1 )
  set( Project_LAST_CHANGED_LOG "unknown changes" )
endif( )

string( REPLACE "\n" "\\\n" LAST_CHANGED_LOG ${Project_LAST_CHANGED_LOG} )
message( "LAST_CHANGED_LOG = \n ${LAST_CHANGED_LOG}" )

function( get_tol_plat var )
  if( CMAKE_SIZEOF_VOID_P EQUAL 4 )
    set( tol_ps 32 )
  elseif( CMAKE_SIZEOF_VOID_P EQUAL 8 )
    set( tol_ps 64 )
  else( )
    message( WARNING "Could not determine pointer size because CMAKE_SIZEOF_VOID_P=${CMAKE_SIZEOF_VOID_P}" )
    set( ${var} "unknown" PARENT_SCOPE )
    return()    
  endif( )
  string( TOUPPER ${CMAKE_SYSTEM_NAME} _system )
  if( UNIX )
    if( _system STREQUAL "LINUX" )
      set( ${var} "Linux${tol_ps}GNU" PARENT_SCOPE )
      return()
    else( )
      message( WARNING "Could not determine TOL platform for system ${CMAKE_SYSTEM_NAME}" )
      set( ${var} "unknown" PARENT_SCOPE )
      return()
    endif( )
  elseif( MINGW )
    set( ${var} "Win${tol_ps}GNU" PARENT_SCOPE )
  else( MINGW )
    message( WARNING "Could not determine TOL platform for system ${CMAKE_SYSTEM_NAME}" )
    set( ${var} "unknown" PARENT_SCOPE )
    return()
  endif( UNIX )
endfunction( get_tol_plat )

function( download_tol_package pkg bin )
  # WGET should be previously searched and the variables WGET_EXECUTABLE and WGET_ERROR_MESSAGE defined
  set( url "https://packages.tol-prj.org/OfficialTolArchiveNetwork/repository.php?action=download&format=attachment&tol_package_version=4&package=${pkg}" )
  # Obtains package name for current platform
  if( ${bin} )
    get_tol_plat( tol_plat )
	set( url "${url}%23${tol_plat}" )	
	set( pkg_plat "${pkg}#${tol_plat}" )
	message( STATUS "Downloading last version of '${pkg}' for platform '${tol_plat}' ..." )
  else( )
    set( tol_plat "" )
	set( pkg_plat "${pkg}" )	
	message( STATUS "Downloading last version of '${pkg}' ..." )
  endif( )
  # Removes previous downloaded versions of the package if necessary
  file( GLOB previous_packages "${CMAKE_CURRENT_BINARY_DIR}/${pkg_plat}.*.zip*" )
  foreach( f ${previous_packages} )
    file( REMOVE ${f} )
  endforeach( )
  # Download the requested package
  execute_process( 
    COMMAND ${WGET_EXECUTABLE} --content-disposition "${url}" 
    OUTPUT_VARIABLE WGET_OUTPUT
    ERROR_VARIABLE IGNORE_ERROR
    OUTPUT_STRIP_TRAILING_WHITESPACE )
  # Check if the package is correctly downloaded
  # Execution variables cannot be used to check, since a file is downloaded even when the package is not founded
  file( GLOB downloaded_package "${CMAKE_CURRENT_BINARY_DIR}/${pkg_plat}.*.zip" )
  string( LENGTH "${downloaded_package}" download_check )
  if( ${download_check} EQUAL 0 )
    message( ${WGET_ERROR_MESSAGE} "Package '${pkg}' cannot be downloaded." )	
  else( )
    message( STATUS "Package '${pkg}' correctly downloaded." )
  endif( )
endfunction( download_tol_package )

set( TOL_SUBLIBRARIES "" CACHE INTERNAL "" )
macro( add_tol_sublibrary name )
  add_library( ${name} OBJECT ${ARGN} )
  #message( STATUS "add_library( ${name} OBJECT ${ARGN} )" )
  if( CMAKE_BUILD_TYPE MATCHES Debug )
    #set_property(TARGET ${name} PROPERTY COMPILE_FLAGS "-fPIC -Wall")
    target_compile_options( ${name} PRIVATE "-Wall" )
  endif( CMAKE_BUILD_TYPE MATCHES Debug )
  if( NEED_FPIC )
    target_compile_options( ${name} PRIVATE "-fPIC" )
  endif( NEED_FPIC )
  if( TOL_SUBLIBRARIES STREQUAL "" )
    set( TOL_SUBLIBRARIES $<TARGET_OBJECTS:${name}> CACHE INTERNAL "" )
  else( )
    set( TOL_SUBLIBRARIES "${TOL_SUBLIBRARIES};$<TARGET_OBJECTS:${name}>" 
      CACHE INTERNAL "" )
  endif( )
endmacro( )

set( skip_copy_headers 0 )

set( COPY_HEADERS_DEPENDS "" CACHE INTERNAL "" )
macro( create_header_links target )
  if( WIN32 )
    set( _cmd "copy" )
  else( WIN32 )
    set( _cmd "create_symlink" )
  endif( WIN32 )
  file( GLOB headers RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" *.h *.hpp )
  install( FILES ${headers} DESTINATION "include/tol"
        COMPONENT DEVELOPMENT )
  if( skip_copy_headers )
    message( STATUS "Skipping create_header_links for ${CMAKE_CURRENT_SOURCE_DIR}" )
  else( skip_copy_headers )
    set( lst  "" )
    foreach( h ${headers} )
      set( srcFile "${CMAKE_CURRENT_SOURCE_DIR}/${h}" )
      set( destFile "${PROJECT_BINARY_DIR}/tol/${h}" )
      set( lst ${lst} "${destFile}" )
      add_custom_command ( 
        OUTPUT ${destFile}
        COMMAND "${CMAKE_COMMAND}" "-E" ${_cmd} ${srcFile} ${destFile}
        DEPENDS ${srcFile}
        COMMENT "Creating header ${destFile}" )
    endforeach( )
    string( REPLACE "${PROJECT_SOURCE_DIR}" "" COPY_TARGET_NAME "COPY_HDR${CMAKE_CURRENT_SOURCE_DIR}" )
    string( REPLACE "/" "_" COPY_TARGET_NAME "${COPY_TARGET_NAME}" )
    #message( "add_custom_target( ${COPY_TARGET_NAME} ALL DEPENDS ${lst} ) " )
    add_custom_target( ${COPY_TARGET_NAME} ALL DEPENDS ${lst} )  
    add_dependencies(${target} ${COPY_TARGET_NAME})
    set( tmp ${COPY_HEADERS_DEPENDS} ${COPY_TARGET_NAME} )
    set( COPY_HEADERS_DEPENDS ${tmp} CACHE INTERNAL "" )
  endif( skip_copy_headers )
endmacro( create_header_links )

# create directory where the headers are goind to be linked.
file( MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tol )

include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
#include_directories( "${PROJECT_SOURCE_DIR}/contrib" )

include( CheckIncludeFiles )
include( CheckFunctionExists )
check_include_files( "utime.h" HAVE_UTIME_H )
check_include_files( "sys/utime.h" HAVE_SYS_UTIME_H )
check_include_files( "dirent.h" HAVE_DIRENT_H )
check_include_files( "sys/ndir.h" HAVE_SYS_NDIR_H )
check_include_files( "sys/dir.h" HAVE_SYS_DIR_H )
check_include_files( "ndir.h" HAVE_NDIR_H )
check_include_files( "string.h" HAVE_STRING_H )
check_include_files( "strings.h" HAVE_STRINGS_H )
check_include_files( "unistd.h" HAVE_UNISTD_H )
check_include_files( "sys/time.h" HAVE_SYS_TIME_H )
check_include_files( "time.h" HAVE_TIME_H )
check_include_files( "stdlib.h" HAVE_STDLIB_H )
check_include_files( "limits.h" HAVE_LIMITS_H )
check_include_files( "float.h" HAVE_FLOAT_H )
check_include_files( "values.h" HAVE_VALUES_H )
check_include_files( "varargs.h" HAVE_VARARGS_H )

set( CMAKE_REQUIRED_LIBRARIES m )
check_function_exists( round HAVE_ROUND )

string( TIMESTAMP build_date "%Y-%m-%d" UTC )
string( TIMESTAMP build_time "%H:%M:%S" UTC )

# -fno-strict-aliasing, and it is not a workaround for a warning: it removes a
# permission the optimizer has and this code does not want it to have.
#
# The matrix layer reinterprets a BMatrix<BDat>& as a BMatrix<double>& through
#   #define b2dMat(M) ((DMat&)(M))
# in btol/matrix_type/matgra.cpp, and that idiom is used in eight files across
# three directories -- matgra.cpp alone has 48 uses. The cast is sound in
# practice, because BDat has a single non-static member, a BReal that is a
# double, so the layouts really do match. What is not sound is the aliasing
# rule: they are two unrelated class types, -fstrict-aliasing is on by default
# from -O2, and there the compiler may assume the two references cannot name the
# same object. It is the same shape of problem as the BSys::MkDir without a
# return: legal until an optimizer exercises the permission it was given.
#
# The alternative was rejected on cost. tol_bmatimp.h declares BMatBDat2Double
# and BMatDouble2BDat for exactly this, but they Alloc and memcpy the whole
# matrix, and the macro is used inside loops: that trades a free cast for a copy
# per operation. Turning the rule off costs nothing per operation and only
# forgoes some aliasing-based optimisations. Decided by the author of TOL on
# 2026-08-24 with the cost measured and stated.
#
# Compiler-wide and not per file on purpose: with the idiom in eight files, a
# per-file list is a trap for whoever adds the ninth.
if( CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" )
  add_compile_options( -fno-strict-aliasing )
endif( )

add_definitions( -DHAVE_CONFIG_H -DUSE_DELAY_INIT -D__USE_ZIP_ARCHIVE__
  -D__USE_DYNSCOPE__
  -D__HASH_MAP_MSVC__=1 -D__HASH_MAP_GOOGLE__=2 -D__HASH_MAP_GCC__=3 
  -D__HASH_MAP_ICC__=4 -D__USE_HASH_MAP__=__HASH_MAP_GOOGLE__ )

# TOL brings its own fixed size memory pool, used by 106 classes of the
# interpreter. It can be turned off to fall back on plain malloc/free, which is
# useful both to tell pool bugs apart from everything else and to measure what
# the pool is still worth: a 2026 malloc is not a 1996 one.
# Measured on 2026-08-10, Ubuntu 24.04, GCC 13.3, Release, five interleaved runs
# of a single-process benchmark allocating 120.000 objects: without the pool TOL
# is 13% faster (median 0,83 s against 0,95 s) and uses 17% less memory (159 MB
# against 191 MB), winning all five pairs. The pool was written when malloc was
# slow and had no per-thread caches nor size classes of its own; glibc has since
# grown exactly those. So it is off by default on glibc.
#
# Not so on MinGW, where nobody has measured it yet and the C runtime allocator
# is a different animal. It stays on there until someone repeats the benchmark.
if( MINGW )
  option( TOL_USE_MEMORY_POOL "Use TOL's own fixed size memory pool" ON )
else( )
  option( TOL_USE_MEMORY_POOL "Use TOL's own fixed size memory pool" OFF )
endif( )
if( TOL_USE_MEMORY_POOL )
  set( TOL_POOL_VALUE "__POOL_BFSMEM__" )
else( )
  message( STATUS "TOL's memory pool is DISABLED: using plain malloc/free" )
  set( TOL_POOL_VALUE "__POOL_NONE__" )
endif( )

if( CMAKE_SYSTEM_NAME STREQUAL "Linux" )
  add_definitions( -DUNIX )
  #set( CMAKE_SHARED_LINKER_FLAGS "-rdynamic ${CMAKE_SHARED_LINKER_FLAGS}" )
endif( )

if ( MINGW )
  # http://stackoverflow.com/questions/14181351/i-got-duplicate-section-errors-when-compiling-boost-regex-with-size-optimizati
  # add_definitions( -fno-tree-vectorize )
  # add_definitions( -fno-implicit-templates )
endif( MINGW )

# GSL configuration
find_package( GSL REQUIRED )

if( GSL_FOUND AND TRACE_COMPONENTS_FOUND )
  message("GSL_INCLUDE_DIRS = ${GSL_INCLUDE_DIRS}")
  message("GSL_LIBRARY = ${GSL_LIBRARY}")
  message("GSL_LIBRARIES = ${GSL_LIBRARIES}")
endif( )

# CBLAS configuration
find_package( CBLAS )
if( CBLAS_FOUND )
  set( HAVE_CBLAS_H 1 CACHE INTERNAL "" )
  set( TOL_CBLAS_LIBRARIES ${CBLAS_LIBRARIES} )
  set( TOL_CBLAS_INCLUDE_DIR ${CBLAS_INCLUDE_DIR} )
else( CBLAS_FOUND )
  message( STATUS "CBLAS not found, using gslcblas = ${GSLCBLAS_LIBRARY}" )
  unset( HAVE_CBLAS_H CACHE )
  set( TOL_CBLAS_LIBRARIES ${GSLCBLAS_LIBRARY} CACHE INTERNAL "" )
  set( TOL_CBLAS_INCLUDE_DIR ${GSL_INCLUDE_DIRS} CACHE INTERNAL "")
endif( CBLAS_FOUND )
if( TRACE_COMPONENTS_FOUND )
  message( "TOL_CBLAS_INCLUDE_DIR = ${TOL_CBLAS_INCLUDE_DIR}" )
  message( "TOL_CBLAS_LIBRARIES = ${TOL_CBLAS_LIBRARIES}" )
endif( TRACE_COMPONENTS_FOUND )

# CLAPACK configuration
find_package( CLAPACK REQUIRED )

if( CLAPACK_FOUND AND TRACE_COMPONENTS_FOUND )
  message("CLAPACK_INCLUDE_DIR = ${CLAPACK_INCLUDE_DIR}")
  message("CLAPACK_LIBRARY = ${CLAPACK_LIBRARY}")
  message("CLAPACK_LIBRARIES = ${CLAPACK_LIBRARIES}")
  if( CLAPACK_NAME STREQUAL "lapacke" )
    set( HAVE_LAPACKE_H 1 CACHE INTERNAL "" )
  else( CLAPACK_NAME STREQUAL "lapacke" )
    unset( HAVE_LAPACKE_H CACHE )
  endif( CLAPACK_NAME STREQUAL "lapacke" )
  include_directories( "${CLAPACK_INCLUDE_DIR}" )
endif( )

# BZIP2 configuration
find_package( BZip2 REQUIRED )

if( BZip2_FOUND AND TRACE_COMPONENTS_FOUND )
  message( "BZIP2_INCLUDE_DIR = ${BZIP2_INCLUDE_DIR}" )
  message( "BZIP2_LIBRARIES = ${BZIP2_LIBRARIES}" )
endif( )

# BOOST configuration
# Boost.System is header only since Boost 1.69 and Ubuntu 26.04, which carries
# Boost 1.90, no longer ships libboost-system-dev nor its CMake config package,
# so asking for it as a component aborts the configuration there. It contributes
# no library to Boost_LIBRARIES on the releases that still have it.
find_package( Boost REQUIRED COMPONENTS thread filesystem )

if( Boost_FOUND AND TRACE_COMPONENTS_FOUND )
  message( "Boost_INCLUDE_DIRS = ${Boost_INCLUDE_DIRS}" )
  message( "Boost_LIBRARIES = ${Boost_LIBRARIES}" )
endif( )

include_directories( ${Boost_INCLUDE_DIRS} )

find_package( BLAS REQUIRED )
if ( BLAS_FOUND AND TRACE_COMPONENTS_FOUND )
  message( STATUS "BLAS_LIBRARIES = ${BLAS_LIBRARIES}")
endif()

# BLAS & LAPACK configuration
find_package( LAPACK REQUIRED )
if( LAPACK_FOUND AND TRACE_COMPONENTS_FOUND )
  message( STATUS "LAPACK_LIBRARIES = ${LAPACK_LIBRARIES}")
endif()

# ODBC configuration
find_package( ODBC )

if( ODBC_FOUND AND TRACE_COMPONENTS_FOUND )
  message( "ODBC_INCLUDE_DIR = ${ODBC_INCLUDE_DIR}" )
  message( "ODBC_LIBRARIES = ${ODBC_LIBRARIES}" )
endif( )

# PostgreSQL configuration, be carefull the library path may be
# pointing to 64 bit release as it depends on pg_config output.  Note
# that we are using the FindPostgreSQL.cmake distributed by VTK
find_package( PostgreSQL )

if( PostgreSQL_FOUND AND TRACE_COMPONENTS_FOUND )
  message( "PostgreSQL_INCLUDE_DIRS = ${PostgreSQL_INCLUDE_DIRS}" )
  message( "PostgreSQL_LIBRARY = ${PostgreSQL_LIBRARY}" )
  message( "PostgreSQL_LIBRARIES = ${PostgreSQL_LIBRARIES}" )
  message( "PostgreSQL_LIBRARY_DIRS =${PostgreSQL_LIBRARY_DIRS}" )
endif( )

# MySQL configuration
find_package( MySQL )
if( MYSQL_FOUND AND TRACE_COMPONENTS_FOUND )
  message( "MYSQL_INCLUDE_DIRECTORIES = ${MYSQL_INCLUDE_DIRECTORIES}" )
  message( "MYSQL_LIBRARY = ${MYSQL_LIBRARY}" )
  message( "MYSQL_EXTRA_LIBRARIES = ${MYSQL_EXTRA_LIBRARIES}" )
endif( )

# FFTW configuration
find_package( FFTW REQUIRED )
if( FFTW_FOUND AND TRACE_COMPONENTS_FOUND )
  message( "FFTW_INCLUDES = ${FFTW_INCLUDES}" )
  message( "FFTW_LIBRARIES = ${FFTW_LIBRARIES}" )
endif( )

# CINT configuration
find_package( CintHeaders )
if( CINT_HEADERS_FOUND )
  if( TRACE_COMPONENTS_FOUND )
    message( "CINT_INCLUDE_DIR = ${CINT_INCLUDE_DIR}" )
  endif( )
  set( HAVE_CINT 1 CACHE INTERNAL "" )
else( )
  unset( HAVE_CINT CACHE )
endif( )

# ZIPARCHIVE configuration
if(UNIX)
  message(STATUS "On UNIX ZIPARCHIVE will be cmopiled as ExternalProject")
else(UNIX)
  find_package( ZipArchive REQUIRED )
  if( ZIPARCHIVE_FOUND AND TRACE_COMPONENTS_FOUND )
    message( "ZIPARCHIVE_INCLUDE_DIR = ${ZIPARCHIVE_INCLUDE_DIR}" )
    message( "ZIPARCHIVE_LIBRARY = ${ZIPARCHIVE_LIBRARY}" )
  endif( )
endif(UNIX)

# ZLIB configuration
find_package( ZLIB REQUIRED )
if( ZLIB_FOUND AND TRACE_COMPONENTS_FOUND )
  message( "ZLIB_INCLUDE_DIRS = ${ZLIB_INCLUDE_DIRS}" )
  message( "ZLIB_LIBRARIES = ${ZLIB_LIBRARIES}" )
endif( )

# CHOLMOD configuration
find_package( CHOLMOD REQUIRED )
if( CHOLMOD_FOUND AND TRACE_COMPONENTS_FOUND )
  message( "CHOLMOD_INCLUDE_DIR = ${CHOLMOD_INCLUDE_DIR}" )
  message( "CHOLMOD_LIBRARIES = ${CHOLMOD_LIBRARIES}" )
endif( )

# LTDL configuration
find_package( LTDL REQUIRED )
if( LTDL_FOUND AND TRACE_COMPONENTS_FOUND )
  message( "LTDL_INCLUDE_DIR = ${LTDL_INCLUDE_DIR}" )
  message( "LTDL_LIBRARIES = ${LTDL_LIBRARIES}" )
endif( )

if( NOT DEFINED WRITE_CONFIG_H )
  set( WRITE_CONFIG_H 1 )
endif( NOT DEFINED WRITE_CONFIG_H )
  
if( ${WRITE_CONFIG_H} )
  message( STATUS "Invoking CONFIGURE_FILE" )
  CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in 
    ${CMAKE_CURRENT_BINARY_DIR}/tol/config.h )
  # tol_bcommon.h is a public installed header and it does #include <tol/config.h>,
  # so the generated file has to travel with the rest of them. It used to be
  # installed by accident: a generated config.h was checked into the source tree
  # and the file( GLOB headers *.h ) above swept it up. Once that copy was
  # removed, toltcl and tolbase stopped finding it.
  install( FILES "${CMAKE_CURRENT_BINARY_DIR}/tol/config.h"
    DESTINATION "include/tol" COMPONENT DEVELOPMENT )
endif( )

foreach( d 
    contrib bmath bparser btol dbdrivers lang LoadDynLib  
    PackArchive OIS bbasic )
  add_subdirectory(${d})
#  get_property(type TARGET ${d} PROPERTY TYPE)
#  message("type(${d}) = '${type}'") 
#  if( type STREQUAL "OBJECT_LIBRARY" )
#    list( APPEND tol_sublibs $<TARGET_OBJECTS:${d}> )
#  else( )
#    list( APPEND tol_sublibs ${d} )
#  endif( )
endforeach( )

if( WIN32 )
  add_tol_sublibrary( tol_init toldll.cpp init.cpp )
else( WIN32 )
  add_tol_sublibrary( tol_init init.cpp )
endif( WIN32 )
target_include_directories( tol_init PRIVATE
    ${CHOLMOD_INCLUDE_DIR} )

create_header_links( tol_init )

add_dependencies(bbasic tol_init)
add_dependencies(tol_init COPY_HEADERS)
# Solo en UNIX: prj_zipa es el ExternalProject que compila ZipArchive desde
# tol/contrib/ZipArchive, y arriba solo se crea en la rama UNIX. Fuera de ella el
# objetivo no existe, y depender de un objetivo inexistente era tolerado por las
# versiones antiguas de CMake pero desde la politica CMP0046 es un error. Es lo
# primero que impide configurar una compilacion cruzada a Windows.
if( UNIX )
  add_dependencies(lang prj_zipa)
endif( UNIX )

if( UNIX )
  SET(CMAKE_INSTALL_RPATH "$ORIGIN") 
endif( UNIX )
add_library( tol SHARED ${TOL_SUBLIBRARIES} )
if( WIN32 )
  set( PLATFORM_LIBRARIES ws2_32 )
endif( WIN32 )
target_link_libraries( tol 
  ${GSL_LIBRARY} ${TOL_CBLAS_LIBRARIES} ${CLAPACK_LIBRARIES}
  ${BZIP2_LIBRARIES} ${Boost_LIBRARIES}
  ${FFTW_LIBRARIES} ${ZLIB_LIBRARIES}
  ${ZIPARCHIVE_LIBRARY} ${CHOLMOD_LIBRARIES} ${LTDL_LIBRARIES}
  ${PLATFORM_LIBRARIES} ${LAPACK_LIBRARIES} )

if( UNIX )
  SET(CMAKE_INSTALL_RPATH "$ORIGIN/../lib") 
endif( UNIX )

# Paso 3.3: la pagina ANSI del proceso en UTF-8, por manifiesto. Los porques y
# las medidas estan en tol/tol-utf8.manifest.
if( MINGW )
  set( CMAKE_RC_COMPILER_INIT windres )
  enable_language( RC )
  # La regla por defecto le pasa a windres <FLAGS>, que son las banderas del
  # compilador de C, y windres muere con "invalid option -- W". Se le pasan los
  # <DEFINES> --que son -D y si los entiende-- y nada mas.
  if( CMAKE_SIZEOF_VOID_P EQUAL 4 )
    set( CMAKE_RC_COMPILE_OBJECT
         "<CMAKE_RC_COMPILER> -O coff <DEFINES> -i <SOURCE> -o <OBJECT> -F pe-i386" )
  else( CMAKE_SIZEOF_VOID_P EQUAL 4 )
    set( CMAKE_RC_COMPILE_OBJECT
         "<CMAKE_RC_COMPILER> -O coff <DEFINES> -i <SOURCE> -o <OBJECT> -F pe-x86-64" )
  endif( CMAKE_SIZEOF_VOID_P EQUAL 4 )
  set( TOL_UTF8_MANIFEST "${CMAKE_CURRENT_SOURCE_DIR}/tol-utf8.manifest" )
  configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/tol-utf8.rc.in"
                  "${CMAKE_CURRENT_BINARY_DIR}/tol-utf8.rc" @ONLY )
  set( TOL_UTF8_RC "${CMAKE_CURRENT_BINARY_DIR}/tol-utf8.rc" )
  # El .rc generado solo NOMBRA al manifiesto, asi que sin esto cambiar el
  # manifiesto no recompila nada y se mide un binario viejo creyendolo nuevo.
  set_source_files_properties( ${TOL_UTF8_RC} PROPERTIES
                               OBJECT_DEPENDS "${TOL_UTF8_MANIFEST}" )
else( MINGW )
  set( TOL_UTF8_RC "" )
endif( MINGW )
add_executable( tolcon tol.cpp ${TOL_UTF8_RC} )
target_link_libraries( tolcon tol )

#message( STATUS "ALL_HEADERS_LIST ${ALL_HEADERS_LIST}" )
add_custom_target( COPY_HEADERS ALL DEPENDS ${COPY_HEADERS_DEPENDS} )

install( TARGETS tolcon RUNTIME DESTINATION bin )

# Several versions of TOL have to be able to coexist, so the prefix is versioned
# (/usr/local/tol4.1) and the executables end up somewhere that is not on anyone's
# PATH. Making them reachable was, until now, a manual step: the .deb of 3.5
# carried no postinst and the /usr/local/bin/tolsh symlink was created by hand.
# That is not an installation. Point TOL_SYMLINK_DIR at a directory on the PATH
# and the links become part of "make install".
set( TOL_SYMLINK_DIR "" CACHE PATH
  "Directory on the PATH where install puts symlinks to the executables" )
if( TOL_SYMLINK_DIR )
  install( CODE "
    file( MAKE_DIRECTORY \"${TOL_SYMLINK_DIR}\" )
    file( CREATE_LINK
      \"\${CMAKE_INSTALL_PREFIX}/bin/tolcon\"
      \"${TOL_SYMLINK_DIR}/tolcon\" SYMBOLIC )
    message( STATUS \"Symlinked ${TOL_SYMLINK_DIR}/tolcon\" )
  " COMPONENT RUNTIME )
endif( )
install( TARGETS tol
  RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )

install( DIRECTORY stdlib COMPONENT RUNTIME DESTINATION "bin" 
  PATTERN "*.tol" 
  PATTERN "*.bst"
  PATTERN "*.oza"
  PATTERN .svn EXCLUDE )

if( WIN32 AND TOL_EXTERNAL_DEVEL_DIR )
  if( NOT EXISTS ${CMAKE_INSTALL_PREFIX}/bin )
    file( MAKE_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin )
  endif( )
  if( NOT EXISTS ${CMAKE_INSTALL_PREFIX}/lib )
    file( MAKE_DIRECTORY ${CMAKE_INSTALL_PREFIX}/lib )
  endif( )
  if( NOT EXISTS ${CMAKE_INSTALL_PREFIX}/include )
    file( MAKE_DIRECTORY ${CMAKE_INSTALL_PREFIX}/include )
  endif( )  
  file( TO_CMAKE_PATH "${TOL_EXTERNAL_PREFIX}/bin/*.dll" dll_pattern )
  file( GLOB RT_DLL  ${dll_pattern} )
  # Boost's DLLs do not always live in bin/: the cross build's dependency tree left
  # them in lib/, so that directory is swept too. Globbed and not named, because the
  # two literal names this used to list do not exist in every tree -- MSYS2 suffixes
  # them "-mt", and Boost has had no separate system library at all since 1.69, so
  # naming libboost_system.dll made file( INSTALL ) fail on a missing file.
  file( GLOB RT_BOOST_DLL "${TOL_EXTERNAL_PREFIX}/lib/libboost_*.dll" )
  list( APPEND RT_DLL ${RT_BOOST_DLL} )
  list( GET RT_DLL 0 rt_dll0)
  get_filename_component( name_rt_dll0 ${rt_dll0} NAME )
  message(  "OJO ${CMAKE_INSTALL_PREFIX}/bin/${name_rt_dll0}")
  if( NOT EXISTS "${CMAKE_INSTALL_PREFIX}/bin/${name_rt_dll0}" )
    file( INSTALL ${RT_DLL} DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" )
  endif( NOT EXISTS "${CMAKE_INSTALL_PREFIX}/bin/${name_rt_dll0}" )
  # The Tcl INTERPRETER, not just its DLL. On Linux it arrives with the system
  # tcl package and sits on the PATH; on Windows there is no such thing, so if it
  # does not travel from here it does not travel at all. What needs it is the
  # toltcl test battery: StdLib::MyTclshPath looks for it next to the TOL
  # executable, and without it the nine tests failed identically without ever
  # having run -- each burning exactly the two seconds of the Sleep that waits
  # for a result file that was never going to appear.
  #
  # Copied unconditionally, with no NOT EXISTS guard: file( INSTALL ) already
  # skips what is up to date, and the guards in this block are precisely how a
  # stale copy of a dependency once survived a fix and sent somebody debugging
  # the wrong file for an afternoon.
  file( GLOB RT_TCLSH "${TOL_EXTERNAL_PREFIX}/bin/tclsh*${CMAKE_EXECUTABLE_SUFFIX}" )
  if( RT_TCLSH )
    file( INSTALL ${RT_TCLSH} DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" )
  else( RT_TCLSH )
    message( WARNING
      "No tclsh in ${TOL_EXTERNAL_PREFIX}/bin: the toltcl battery cannot run." )
  endif( RT_TCLSH )

  if( NOT EXISTS "${CMAKE_INSTALL_PREFIX}/lib/datefield" )
    file( GLOB tcl-libs "${TOL_EXTERNAL_DEVEL_DIR}/tcl-libs/*" )
    file( INSTALL ${tcl-libs} DESTINATION "${CMAKE_INSTALL_PREFIX}/lib" )
  endif( NOT EXISTS "${CMAKE_INSTALL_PREFIX}/lib/datefield" )
  
  set( tcldirs "")
  foreach( pdir tcl8 tk8 dde reg TclCurl twapi )
    file( GLOB pdirx.y RELATIVE "${TOL_EXTERNAL_PREFIX}/lib" "${TOL_EXTERNAL_PREFIX}/lib/${pdir}*" )
	list( APPEND tcldirs ${pdirx.y} )
  endforeach( pdir tcl8 tk8 dde reg TclCurl twapi )
  list( GET tcldirs 0 tcldir0)
  if( NOT EXISTS "${CMAKE_INSTALL_PREFIX}/lib/${tcldir0}" )
    foreach( d ${tcldirs} )
	  file( INSTALL "${TOL_EXTERNAL_PREFIX}/lib/${d}" DESTINATION "${CMAKE_INSTALL_PREFIX}/lib" )
	endforeach( d ${tcldirs} )
  endif( NOT EXISTS "${CMAKE_INSTALL_PREFIX}/lib/${tcldir0}" )
  
  if( NOT EXISTS "${CMAKE_INSTALL_PREFIX}/include/tcl.h" )
    file( GLOB tcl_h "${TOL_EXTERNAL_PREFIX}/include/tcl*.h" )
    file( GLOB tk_h "${TOL_EXTERNAL_PREFIX}/include/tk*.h" )
    file( INSTALL ${tcl_h} DESTINATION "${CMAKE_INSTALL_PREFIX}/include" )
    file( INSTALL ${tk_h} DESTINATION "${CMAKE_INSTALL_PREFIX}/include" )
    file( INSTALL "${TOL_EXTERNAL_PREFIX}/include/X11" DESTINATION "${CMAKE_INSTALL_PREFIX}/include" )
  endif( NOT EXISTS "${CMAKE_INSTALL_PREFIX}/include/tcl.h" )
  if( NOT EXISTS "${CMAKE_INSTALL_PREFIX}/include/google" )
    file( INSTALL
          "${TOL_EXTERNAL_PREFIX}/include/google" 
          "${TOL_EXTERNAL_PREFIX}/include/sparsehash"
          "${TOL_EXTERNAL_PREFIX}/include/libltdl"
          "${TOL_EXTERNAL_PREFIX}/include/ltdl.h"
          "${TOL_EXTERNAL_PREFIX}/include/gsl"
          DESTINATION "${CMAKE_INSTALL_PREFIX}/include" )
  endif( NOT EXISTS "${CMAKE_INSTALL_PREFIX}/include/google" )
  message( STATUS "queee ${CMAKE_INSTALL_PREFIX}/lib/libltdl.dll.a")
  if( NOT EXISTS "${CMAKE_INSTALL_PREFIX}/lib/libltdl.dll.a" )
    file( INSTALL 
          "${TOL_EXTERNAL_PREFIX}/lib/libltdl.dll.a" 
          "${TOL_EXTERNAL_PREFIX}/lib/libgsl.dll.a"
          "${TOL_EXTERNAL_PREFIX}/lib/libgslcblas.dll.a"
		  "${TOL_EXTERNAL_PREFIX}/lib/libblas.dll.a"
		  "${TOL_EXTERNAL_PREFIX}/lib/liblapack.dll.a"
		  "${TOL_EXTERNAL_PREFIX}/lib/liblapacke.dll.a"
          DESTINATION "${CMAKE_INSTALL_PREFIX}/lib" )
  endif( NOT EXISTS "${CMAKE_INSTALL_PREFIX}/lib/libltdl.dll.a" )
  file( GLOB tcl_a "${TOL_EXTERNAL_PREFIX}/lib/libtcl*.a" )
  message( "tcl_a = ${tcl_a}")
  list( GET tcl_a 0 tcl_a0 )
  get_filename_component( name_tcl_a0 ${tcl_a0} NAME )
  if( NOT EXISTS "${CMAKE_INSTALL_PREFIX}/lib/${name_tcl_a0}" )
    file( GLOB tk_a "${TOL_EXTERNAL_PREFIX}/lib/libtk*.a" )
    file( INSTALL ${tcl_a} ${tk_a}
          DESTINATION "${CMAKE_INSTALL_PREFIX}/lib" )
  endif( NOT EXISTS "${CMAKE_INSTALL_PREFIX}/lib/${name_tcl_a0}" )  

endif( WIN32 AND TOL_EXTERNAL_DEVEL_DIR )

# TOL_DEFAULT_PACKAGES_DIR takes the default packages from a local directory
# instead of downloading them, and there are two solid reasons to want that.
#
#  * The package archive is only reachable over the VPN, so as it stands the
#    configuration step cannot run on a hosted CI runner at all.
#
#  * The StdLib the archive publishes is not necessarily the one in this tree.
#    Both carry the same version number and their .oza differ -- 413653 bytes
#    against 431411 in the pair found on 2026-08-19 -- and whichever ends up in
#    DefaultPackages is the one the engine bootstraps with. Being able to point
#    at a locally built one is the only way to keep the two in step.
#
# To build that StdLib zip, pack it with "tolsh -ndp". Without -ndp the packer
# recompiles StdLib in a session that already has it loaded, its classes cannot
# be redefined -- "Class '@MultiTryMetropolis' ya fue definida" -- and the zip
# comes out short and silently broken.
if( TOL_DEFAULT_PACKAGES_DIR )
  file( GLOB default_zips "${TOL_DEFAULT_PACKAGES_DIR}/*.zip" )
  if( default_zips )
    message( STATUS "Default packages taken from ${TOL_DEFAULT_PACKAGES_DIR}: ${default_zips}" )
    install( FILES ${default_zips}
      DESTINATION bin/stdlib/DefaultPackages
      COMPONENT RUNTIME )
  else( default_zips )
    message( FATAL_ERROR
      "TOL_DEFAULT_PACKAGES_DIR=${TOL_DEFAULT_PACKAGES_DIR} holds no zip. "
      "TOL cannot start without StdLib." )
  endif( default_zips )
else( TOL_DEFAULT_PACKAGES_DIR )

# WGET_ERROR_MESSAGE can be defined for cmaking without errors relative to downloading default packages
# For example: cmake -DWGET_ERROR_MESSAGE=WARNING ...
if( NOT DEFINED WGET_ERROR_MESSAGE )
  set( WGET_ERROR_MESSAGE FATAL_ERROR )
endif( NOT DEFINED WGET_ERROR_MESSAGE )
# Searching WGET command
find_program( WGET_EXECUTABLE NAMES "wget" )
# find_program is used instead find_package, since configuration files are not needed
if( WGET_EXECUTABLE )
  message( STATUS "WGET_EXECUTABLE = ${WGET_EXECUTABLE}" )
  execute_process( 
    COMMAND ${WGET_EXECUTABLE} --help 
    OUTPUT_VARIABLE WGET_HELP
    ERROR_VARIABLE IGNORE_ERROR
    OUTPUT_STRIP_TRAILING_WHITESPACE )
  # wget option 'content-disposition' is required; see download_tol_package
  string(REPLACE "\n" " " WGET_HELP ${WGET_HELP})
  string(FIND ${WGET_HELP} "--content-disposition" WGET_OK)
  if( WGET_OK LESS 0 )
    message( ${WGET_ERROR_MESSAGE} "WGET option 'content-disposition' not available. Install a newer version of WGET." )
  else( WGET_OK LESS 0 )
    # Download default packages
    download_tol_package( StdLib 0 )
    download_tol_package( TclCore 1 )
	# Obtain the list of the downloaded default packages
    file( GLOB default_zips "${CMAKE_CURRENT_BINARY_DIR}/*.zip" )	
    message( STATUS "default_zips = ${default_zips} " )
    if( default_zips )
      install( FILES ${default_zips}
        DESTINATION bin/stdlib/DefaultPackages
        COMPONENT RUNTIME )
    endif( )
  endif( WGET_OK LESS 0 )
else( WGET_EXECUTABLE )
  message( ${WGET_ERROR_MESSAGE} "WGET is not available. Could not download default TOL packages." )
endif( WGET_EXECUTABLE )

endif( TOL_DEFAULT_PACKAGES_DIR )
