CMAKE_MINIMUM_REQUIRED(VERSION 2.3)

# A previously installed Chicken can be used to build a new Chicken.
# The previously installed Chicken must have minimum Version and Build numbers.
SET(MINIMUM_CHICKEN_VERSION 2)
SET(MINIMUM_CHICKEN_BUILD 2)


##############################################################
#                                                            #
#  FIND CHICKEN                                              #
#                                                            #
#  Outputs:  VALID_CHICKEN                                   #
#                                                            #
#  Absolute path of chicken executable.  If none exists,     #
#  evaluates to -NOTFOUND, which is equivalent to FALSE in   #
#  conditional tests.  Thus, VALID_CHICKEN is both the path  #
#  and the boolean about whether the path is usable.  Isn't  #
#  CMake clever?                                             #
#                                                            #
##############################################################


# Is CHICKEN_HOME set in the environment?
# CHICKEN_HOME need not be set, but it could be a configuration error.
# Warn the user if it isn't set, and state the exact error.

STRING(COMPARE NOTEQUAL "$ENV{CHICKEN_HOME}" "" HAVE_CHICKEN_HOME)
IF(HAVE_CHICKEN_HOME)
  MESSAGE(STATUS "CHICKEN_HOME is $ENV{CHICKEN_HOME}")
  IF(NOT EXISTS $ENV{CHICKEN_HOME})
    MESSAGE(STATUS "However, that directory does not exist!")
  ENDIF(NOT EXISTS $ENV{CHICKEN_HOME})
ELSE(HAVE_CHICKEN_HOME)
  MESSAGE(STATUS "CHICKEN_HOME is NOT set in your environment.")
  MESSAGE(STATUS "Searching system PATHs for Chicken.")
ENDIF(HAVE_CHICKEN_HOME)

# Do we have a Chicken installed already that we can use for the build?

FIND_PROGRAM(EXTANT_CHICKEN
  NAMES chicken-static.exe chicken.exe
  PATHS $ENV{CHICKEN_HOME})
#STRING(COMPARE NOTEQUAL "${EXTANT_CHICKEN}" "EXTANT_CHICKEN-NOTFOUND" HAVE_EXTANT_CHICKEN)

SET(VALID_CHICKEN "-NOTFOUND")
IF(EXTANT_CHICKEN)
  MESSAGE(STATUS "Found ${EXTANT_CHICKEN}")
  EXEC_PROGRAM(${EXTANT_CHICKEN}
    ARGS -version
    OUTPUT_VARIABLE CHICKEN_BANNER
    RETURN_VALUE CHICKEN_RETURN)
  IF(CHICKEN_RETURN EQUAL 0)
    # Parse the Chicken banner to extract the version number.
    # This could be error prone if the banner format ever changes.
    SET(VERSION_BUILD_REGEX ".*Version ([0-9]+), Build ([0-9]+).*")
    STRING(REGEX REPLACE ${VERSION_BUILD_REGEX} "\\1"
      VERSION_NUMBER ${CHICKEN_BANNER})
    STRING(REGEX REPLACE ${VERSION_BUILD_REGEX} "\\2"
      BUILD_NUMBER ${CHICKEN_BANNER})
    MESSAGE(STATUS "Chicken ${MINIMUM_CHICKEN_VERSION}.${MINIMUM_CHICKEN_BUILD} required, found ${VERSION_NUMBER}.${BUILD_NUMBER}")

    IF(VERSION_NUMBER GREATER MINIMUM_CHICKEN_VERSION)
      SET(VALID_CHICKEN ${EXTANT_CHICKEN})
    ENDIF(VERSION_NUMBER GREATER MINIMUM_CHICKEN_VERSION)
    IF(VERSION_NUMBER EQUAL MINIMUM_CHICKEN_VERSION)
      IF(NOT BUILD_NUMBER LESS MINIMUM_CHICKEN_BUILD)
        SET(VALID_CHICKEN ${EXTANT_CHICKEN})
      ENDIF(NOT BUILD_NUMBER LESS MINIMUM_CHICKEN_BUILD)
    ENDIF(VERSION_NUMBER EQUAL MINIMUM_CHICKEN_VERSION)

  ELSE(CHICKEN_RETURN EQUAL 0)
    MESSAGE(STATUS "Chicken -version returned bogus result.")
  ENDIF(CHICKEN_RETURN EQUAL 0)

ELSE(EXTANT_CHICKEN)
  MESSAGE(STATUS "Cannot find any previous Chicken installation.")
  IF($ENV{CHICKEN_HOME})
    MESSAGE(STATUS "CHICKEN_HOME is set, but neither chicken-static.exe nor chicken.exe is there.")
  ENDIF($ENV{CHICKEN_HOME})
ENDIF(EXTANT_CHICKEN)

IF(VALID_CHICKEN)
  MESSAGE(STATUS "Using ${VALID_CHICKEN} for Scheme compilation.")
ELSE(VALID_CHICKEN)
  MESSAGE(STATUS "Will attempt to compile Chicken from C sources.")
ENDIF(VALID_CHICKEN)


####################################################################
#  BUILD CHICKEN                                                   #
####################################################################


PROJECT(Chicken)
INCLUDE_DIRECTORIES(${Chicken_SOURCE_DIR}) 


# I removed some of the -D options you had,
# you may want to add some back, but make sure to use TRY_COMPILE stuff so
# this works on all platforms.   


#All our various optional libraries go in here.
SET(EXTRA_LIBS )


# The autoconf build uses this define.
# How is it supposed to be determined?
#ADD_DEFINITIONS(-DC_STACK_GROWS_DOWNWARD)


# Get rid of gazillion MSVC warnings
IF(MSVC)
  # only tell us once about unused local variables
  # unfortunately, MSVC receives this flag and ignores it.  Bah!
  ADD_DEFINITIONS(/Wo4101)
ENDIF(MSVC)


# Does MinGW need some of these flags?
IF(MSVC)
  ADD_DEFINITIONS(-DC_DEFAULT_TARGET_STACK_SIZE=300000)
  ADD_DEFINITIONS(-DHAVE_LOADLIBRARY)
  ADD_DEFINITIONS(-DHAVE_GETPROCADDRESS)
ENDIF(MSVC)


# check for socklen_t type
INCLUDE(CheckTypeSize)
IF(WIN32 AND NOT CYGWIN)
  # This isn't working.
  SET(CMAKE_EXTRA_INCLUDE_FILES WS2tcpip.h)
ELSE(WIN32 AND NOT CYGWIN)
  SET(CMAKE_EXTRA_INCLUDE_FILES netdb.h)
ENDIF(WIN32 AND NOT CYGWIN)
CHECK_TYPE_SIZE(socklen_t SIZEOF_SOCKLEN_T)
IF(NOT SIZEOF_SOCKLEN_T)
#  Generates an error on MSVC.
#  Need a cleaner, mo' betta way to do this.
#  Probably means doing more work to find the right .h files.
#  ADD_DEFINITIONS(-Dsocklen_t=int)
ENDIF(NOT SIZEOF_SOCKLEN_T)


# check for grp.h
INCLUDE(CheckIncludeFile)
CHECK_INCLUDE_FILE(grp.h HAVE_GRP_H)
IF(HAVE_GRP_H)
  ADD_DEFINITIONS(-DHAVE_GRP_H)
ENDIF(HAVE_GRP_H)


# check for pcre.h
CHECK_INCLUDE_FILE(pcre.h HAVE_PCRE_H)
IF(HAVE_PCRE_H)
  ADD_DEFINITIONS(-DHAVE_PCRE_H)
  SET(REGEX_SOURCE pcre.c)
  SET(REGEX_UNSAFE_SOURCE upcre.c)
  SET(EXTRA_LIBS ${EXTRA_LIBS} pcre)
ELSE(HAVE_PCRE_H)
  IF(WIN32 AND NOT CYGWIN)
    SET(REGEX_SOURCE pregexp.c)
    SET(REGEX_UNSAFE_SOURCE upregexp.c)
  ELSE(WIN32 AND NOT CYGWIN)
    SET(REGEX_SOURCE regex.c)
    SET(REGEX_UNSAFE_SOURCE uregex.c)
  ENDIF(WIN32 AND NOT CYGWIN)
ENDIF(HAVE_PCRE_H)


# check for dlfcn.h or dl.h
CHECK_INCLUDE_FILE(dlcfn.h HAVE_DLFCN_H)
IF(HAVE_DLFCN_H)
  ADD_DEFINITIONS(-DHAVE_DLFCN_H)
  SET(EXTRA_LIBS ${EXTRA_LIBS} dl)
ENDIF(HAVE_DLFCN_H)
CHECK_INCLUDE_FILE(dl.h HAVE_DL_H)
IF(HAVE_DL_H)
  ADD_DEFINITIONS(-DHAVE_DL_H)
  SET(EXTRA_LIBS ${EXTRA_LIBS} ldl)
ENDIF(HAVE_DL_H)


# check for ffi.h
CHECK_INCLUDE_FILE(ffi.h HAVE_FFI_H)
IF(HAVE_FFI_H)
  ADD_DEFINITIONS(-DHAVE_FFI_H)
  SET(EXTRA_LIBS ${EXTRA_LIBS} ffi)
ENDIF(HAVE_FFI_H)


# check for windows.h
CHECK_INCLUDE_FILE(windows.h HAVE_WINDOWS_H)
IF(HAVE_WINDOWS_H)
  ADD_DEFINITIONS(-DHAVE_WINDOWS_H)
ENDIF(HAVE_WINDOWS_H)


# give the user an option to enable support for procedure-serialiazation
OPTION(ENABLE_PROCEDURE_TABLES "enable support for serialization of procedures" FALSE)
IF(ENABLE_PROCEDURE_TABLES)
  ADD_DEFINITIONS(-DC_ENABLE_PTABLES)
ENDIF(ENABLE_PROCEDURE_TABLES)


# create a list of all the sources
SET (CHICKEN_LIB_SOURCES    
    runtime.c           library.c 
    eval.c              profiler.c 
    scheduler.c         extras.c
    match-support.c     lolevel.c
    stub.c              tinyclos.c
    ${REGEX_SOURCE}     utils.c
    tcp.c               
    srfi-1.c            srfi-4.c
    srfi-13.c           srfi-14.c
    srfi-18.c)
SET (CHICKEN_UNSAFE_LIB_SOURCES    
    runtime.c           ulibrary.c 
    ueval.c              profiler.c 
    scheduler.c         uextras.c
    umatch-support.c     ulolevel.c
    stub.c              utinyclos.c
    ${REGEX_UNSAFE_SOURCE}     uutils.c
    utcp.c               
    usrfi-1.c            usrfi-4.c
    usrfi-13.c           usrfi-14.c
    usrfi-18.c)
SET (CHICKEN_GUI_LIB_SOURCES    
    runtime.c           gui-library.c 
    eval.c              profiler.c 
    scheduler.c         extras.c
    match-support.c    lolevel.c
    stub.c              tinyclos.c
    ${REGEX_SOURCE}     utils.c
    tcp.c               
    srfi-1.c            srfi-4.c
    srfi-13.c           srfi-14.c
    srfi-18.c)


# add a posix source based on OS
IF(WIN32 AND NOT CYGWIN)
  SET (CHICKEN_LIB_SOURCES    ${CHICKEN_LIB_SOURCES}  posixwin.c)
  SET (CHICKEN_UNSAFE_LIB_SOURCES    ${CHICKEN_UNSAFE_LIB_SOURCES}  uposixwin.c)
ENDIF(WIN32 AND NOT CYGWIN)
IF(UNIX)
  SET (CHICKEN_LIB_SOURCES ${CHICKEN_LIB_SOURCES} posix.c)
  SET (CHICKEN_UNSAFE_LIB_SOURCES ${CHICKEN_UNSAFE_LIB_SOURCES} uposix.c)
ENDIF(UNIX)


# since libchicken has to be the name of the dll or .so to load
# On unix lib is the default prefix added to any library.
# For windows we change the name of the library to be libchicken.
# Note: GCC based compilers, i.e. Cygwin, MinGW, behave like Unix.
# In general, be careful about whether NOT CYGWIN or
# NOT CMAKE_COMPILER_IS_GNUCC is really intended.
# Setting the PREFIX to "" works on all platforms.


# create the chicken library
ADD_LIBRARY(libchicken SHARED ${CHICKEN_LIB_SOURCES})
SET_TARGET_PROPERTIES(libchicken PROPERTIES
  PREFIX "" IMPORT_PREFIX "")
ADD_LIBRARY(libchicken-static STATIC ${CHICKEN_LIB_SOURCES})
SET_TARGET_PROPERTIES(libchicken-static PROPERTIES
  PREFIX "" IMPORT_PREFIX "")
ADD_LIBRARY(libuchicken SHARED ${CHICKEN_UNSAFE_LIB_SOURCES})
SET_TARGET_PROPERTIES(libuchicken PROPERTIES
  PREFIX "" IMPORT_PREFIX "")
ADD_LIBRARY(libuchicken-static STATIC ${CHICKEN_UNSAFE_LIB_SOURCES})
SET_TARGET_PROPERTIES(libuchicken-static PROPERTIES
  PREFIX "" IMPORT_PREFIX "")
IF(WIN32 AND NOT CYGWIN)
  ADD_LIBRARY(libchicken_gui SHARED ${CHICKEN_LIB_SOURCES})
  SET_TARGET_PROPERTIES(libchicken_gui PROPERTIES
    PREFIX "" IMPORT_PREFIX "")
  ADD_LIBRARY(libchicken_gui-static STATIC ${CHICKEN_LIB_SOURCES})
  SET_TARGET_PROPERTIES(libchicken_gui-static PROPERTIES
    PREFIX "" IMPORT_PREFIX "")
ENDIF(WIN32 AND NOT CYGWIN)


# Shared and static libraries are built with different flags.
# In CMake, SET(x a b c) produces x="a;b;c"
# SET(x "a b c") produces x="a b c", which is what we want.
SET(SHARED_FLAGS -DPIC)
  IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
    SET(SHARED_FLAGS "${SHARED_FLAGS} -fno-common -no-cpp-precomp")
  ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
SET(STATIC_FLAGS -DC_NO_PIC_NO_DLL)

# tell it to define C_BUILDING_LIBCHICKEN when building .o files that go in chicken_lib
SET_TARGET_PROPERTIES(libchicken PROPERTIES COMPILE_FLAGS
  "-DC_BUILDING_LIBCHICKEN ${SHARED_FLAGS}")
SET_TARGET_PROPERTIES(libchicken-static PROPERTIES COMPILE_FLAGS
  "-DC_BUILDING_LIBCHICKEN ${STATIC_FLAGS}")
SET_TARGET_PROPERTIES(libuchicken PROPERTIES COMPILE_FLAGS
  "-DC_BUILDING_LIBCHICKEN -DC_UNSAFE_RUNTIME ${SHARED_FLAGS}")
SET_TARGET_PROPERTIES(libuchicken-static PROPERTIES COMPILE_FLAGS
  "-DC_BUILDING_LIBCHICKEN -DC_UNSAFE_RUNTIME ${STATIC_FLAGS}")
IF(WIN32 AND NOT CYGWIN)
  SET_TARGET_PROPERTIES(libchicken_gui PROPERTIES COMPILE_FLAGS
    "-DC_BUILDING_LIBCHICKEN -DC_WINDOWS_GUI ${SHARED_FLAGS}")
  SET_TARGET_PROPERTIES(libchicken_gui-static PROPERTIES COMPILE_FLAGS
    "-DC_BUILDING_LIBCHICKEN -DC_WINDOWS_GUI ${STATIC_FLAGS}")
ENDIF(WIN32 AND NOT CYGWIN)



# add ws2_32 for windows and -lm for unix
IF(WIN32)
  # Here, we actually want set(x a b c), no quotes
  SET(EXTRA_LIBS ${EXTRA_LIBS} ws2_32)
ELSE(WIN32)
  # Here, we actually want set(x a b c), no quotes
  SET(EXTRA_LIBS ${EXTRA_LIBS} m)
ENDIF(WIN32)
TARGET_LINK_LIBRARIES(libchicken ${EXTRA_LIBS})
TARGET_LINK_LIBRARIES(libchicken-static ${EXTRA_LIBS})
TARGET_LINK_LIBRARIES(libuchicken ${EXTRA_LIBS})
TARGET_LINK_LIBRARIES(libuchicken-static ${EXTRA_LIBS})

# only need libchicken_gui.lib on Windows
# apparently EXTRA_LIBS not needed here?
IF(WIN32 AND NOT CYGWIN)
  # Here, we actually want set(x a b c), no quotes
  SET(GUI_LIBS kernel32 user32 gdi32 ws2_32)
  TARGET_LINK_LIBRARIES(libchicken_gui ${GUI_LIBS})
  TARGET_LINK_LIBRARIES(libchicken_gui-static ${GUI_LIBS})
ENDIF(WIN32 AND NOT CYGWIN)


# source files for chicken.exe 
SET (CHICKEN_EXE_SOURCES 
    chicken.c       support.c
    partition.c     easyffi.c
    compiler.c      optimizer.c
    batch-driver.c  c-platform.c
    c-backend.c     chicken.rc)
# create chicken executable
ADD_EXECUTABLE(chicken ${CHICKEN_EXE_SOURCES})
SET_TARGET_PROPERTIES(chicken PROPERTIES COMPILE_FLAGS "${SHARED_FLAGS}")
TARGET_LINK_LIBRARIES(chicken libchicken)

ADD_EXECUTABLE(chicken-static ${CHICKEN_EXE_SOURCES})
SET_TARGET_PROPERTIES(chicken-static PROPERTIES COMPILE_FLAGS "${STATIC_FLAGS}")
TARGET_LINK_LIBRARIES(chicken-static libchicken-static)


# get the path for chicken after it is built to be used in custom commands
GET_TARGET_PROPERTY(CHICKEN_PATH chicken LOCATION)


# create csc.scm
CONFIGURE_FILE(${Chicken_SOURCE_DIR}/csc.scm.in ${Chicken_BINARY_DIR}/csc.scm)

# Is this macro affected by shared vs. static library flags?
# Where is 'unsafe' getting its input from?
MACRO(GENERATE_CHICKEN_FILE input output prologue)
  IF(${prologue})
    SET(PROLOGUE -prologue ${prologue})
  ELSE(${prologue})
    SET(PROLOGUE )
  ENDIF(${prologue})
  IF(${unsafe})
    SET(UNSAFE -unsafe)
  ELSE(${unsafe})
    SET(UNSAFE )
  ENDIF(${unsafe})
  ADD_CUSTOM_COMMAND(OUTPUT ${output}
    COMMAND ${CHICKEN_PATH}
    ARGS ${input}
    -optimize-level 2 -no-trace -quiet
    -include-path ${Chicken_SOURCE_DIR} 
    -output-file ${output}
    ${UNSAFE}
    ${PROLOGUE}
    DEPENDS ${CHICKEN_PATH}
    )
  SET_SOURCE_FILES_PROPERTIES( ${output} PROPERTIES GENERATED true)    
ENDMACRO(GENERATE_CHICKEN_FILE)


# create csi.c with chicken 
GENERATE_CHICKEN_FILE(
  ${Chicken_SOURCE_DIR}/csi.scm
  ${Chicken_BINARY_DIR}/csi.c
  ${Chicken_SOURCE_DIR}/build.scm)
ADD_EXECUTABLE(csi ${Chicken_BINARY_DIR}/csi.c)
ADD_DEPENDENCIES(csi chicken)
SET_TARGET_PROPERTIES(csi PROPERTIES COMPILE_FLAGS "${SHARED_FLAGS}")
TARGET_LINK_LIBRARIES(csi libchicken)

ADD_EXECUTABLE(csi-static ${Chicken_BINARY_DIR}/csi.c)
ADD_DEPENDENCIES(csi-static chicken)
SET_TARGET_PROPERTIES(csi-static PROPERTIES COMPILE_FLAGS "${STATIC_FLAGS}")
TARGET_LINK_LIBRARIES(csi-static libchicken-static)


# create csc.c with chicken
GENERATE_CHICKEN_FILE(
  ${Chicken_BINARY_DIR}/csc.scm
  ${Chicken_BINARY_DIR}/csc.c
  0)
ADD_EXECUTABLE(csc ${Chicken_BINARY_DIR}/csc.c)
ADD_DEPENDENCIES(csc chicken)
SET_TARGET_PROPERTIES(csc PROPERTIES COMPILE_FLAGS "${SHARED_FLAGS}")
TARGET_LINK_LIBRARIES(csc libchicken)
# Felix says csc-static isn't necessary.

# create chicken-profile.c with chicken
GENERATE_CHICKEN_FILE(
  ${Chicken_SOURCE_DIR}/chicken-profile.scm 
  ${Chicken_BINARY_DIR}/chicken-profile.c
  0)
ADD_EXECUTABLE(chicken-profile ${Chicken_BINARY_DIR}/chicken-profile.c)
ADD_DEPENDENCIES(chicken-profile chicken)
SET_TARGET_PROPERTIES(chicken-profile PROPERTIES COMPILE_FLAGS "${SHARED_FLAGS}")
TARGET_LINK_LIBRARIES(chicken-profile libchicken)


# create chicken-setup.c with chicken
GENERATE_CHICKEN_FILE(
  ${Chicken_SOURCE_DIR}/chicken-setup.scm 
  ${Chicken_BINARY_DIR}/chicken-setup.c
  0)
ADD_EXECUTABLE(chicken-setup ${Chicken_BINARY_DIR}/chicken-setup.c)
ADD_DEPENDENCIES(chicken-setup chicken)
SET_TARGET_PROPERTIES(chicken-setup PROPERTIES COMPILE_FLAGS "${SHARED_FLAGS}")
TARGET_LINK_LIBRARIES(chicken-setup libchicken)


# installation targets
INSTALL_TARGETS(/
  chicken chicken-setup csi chicken-profile csc
  chicken-static csi-static)
INSTALL_FILES(/ .scm chicken-ffi-macros chicken-match-macros chicken-more-macros)
IF(WIN32)
  INSTALL_FILES(/ .bat csibatch)
ENDIF(WIN32)


#INSTALL_TARGETS(/ RUNTIME_DIRECTORY /
#  libchicken libuchicken libchicken-static libuchicken-static)
INSTALL(TARGETS libchicken libuchicken RUNTIME DESTINATION "")
INSTALL(TARGETS libchicken-static libuchicken-static DESTINATION "")
IF(WIN32 AND NOT CYGWIN)
#  INSTALL_TARGETS(/ RUNTIME_DIRECTORY /
#    libchicken_gui libchicken_gui-static)
   INSTALL(TARGETS libchicken_gui RUNTIME DESTINATION "")
   INSTALL(TARGETS libchicken_gui-static DESTINATION "")
ENDIF(WIN32 AND NOT CYGWIN)

INSTALL_FILES(/ .h chicken)
INSTALL_FILES(/ .1 chicken csi csc chicken-setup chicken-profile)
INSTALL_FILES(/ .pdf chicken)


#
# TODO: 
#
# - nsample + nursery benchmarking
# - patching of csc
#