cmake_minimum_required(VERSION 3.12) file(READ ${CMAKE_CURRENT_SOURCE_DIR}/VERSION version) string(STRIP ${version} version) file(READ ${CMAKE_CURRENT_SOURCE_DIR}/RELEASE release) string(STRIP ${release} release) project(chibi-scheme LANGUAGES C VERSION ${version} DESCRIPTION "Chibi-Scheme: minimal r7rs implementation, release: ${release}") include(CheckIncludeFile) include(CheckSymbolExists) include(GNUInstallDirs) include(CMakePackageConfigHelpers) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Build type: None, Debug, Release, RelWithDebInfo, MinSizeRel, or Sanitizer." FORCE) if (NOT EXISTS ${CMAKE_BINARY_DIR}/CMakeCache.txt AND NOT CMAKE_BUILD_TYPE) # CMake doesn't have a default build type, so set one manually set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "" FORCE) endif() # # Features # check_include_file(poll.h HAVE_POLL_H) check_symbol_exists(ntp_gettime sys/timex.h HAVE_NTP_GETTIME) check_symbol_exists(int_least8_t inttypes.h HAVE_STDINT_H) if (WIN32 AND NOT CYGWIN) set(DEFAULT_SHARED_LIBS OFF) else() set(DEFAULT_SHARED_LIBS ON) endif() option(BUILD_SHARED_LIBS "Build chibi-scheme as a shared library" ${DEFAULT_SHARED_LIBS}) option(SEXP_USE_BOEHM "Use Boehm garbage collection library" OFF) if(SEXP_USE_BOEHM) find_library(BOEHMGC gc REQUIRED) find_path(BOEHMGC_INCLUDE NAMES gc/gc.h) endif() set(chibi-scheme-exclude-modules) if(WIN32) set(chibi-scheme-exclude-modules # Following modules are not compatible with Win32 lib/chibi/net.sld lib/chibi/process.sld lib/chibi/stty.sld lib/chibi/system.sld lib/chibi/time.sld lib/chibi/pty.sld) endif() # # Default settings for all targets. We use an interface library here to not # pollute/mutate global settings. Any configuration applied to this library # is propagated to its client targets. # add_library(libchibi-common INTERFACE) target_compile_definitions(libchibi-common INTERFACE SEXP_STATIC_LIBRARY=$> SEXP_USE_DL=$ $<$:BUILDING_DLL=$> SEXP_USE_INTTYPES=$ SEXP_USE_NTPGETTIME=$ $<$>:SEXP_USE_GREEN_THREADS=0> $<$:SEXP_USE_STRING_STREAMS=0> $<$:SEXP_USE_BOEHM=1>) target_compile_options(libchibi-common INTERFACE $<$:-Wall> $<$,$>:-Wall> $<$:-g -fsanitize=address,undefined,integer,float-divide-by-zero,float-cast-overflow,return -fno-omit-frame-pointer>) target_include_directories(libchibi-common INTERFACE ${BOEHMGC_INCLUDE} $ $ $) target_link_libraries(libchibi-common INTERFACE ${BOEHMGC} $<$:-fsanitize=address,undefined> $<$:ws2_32> $<$,$>:${CMAKE_DL_LIBS}> $<$:m>) # # Sources # set(chibi-scheme-srcs # SEXP gc.c sexp.c bignum.c gc_heap.c # Eval opcodes.c vm.c eval.c simplify.c) # # Bootstrap # add_executable(chibi-scheme-bootstrap EXCLUDE_FROM_ALL ${chibi-scheme-srcs} main.c) target_link_libraries(chibi-scheme-bootstrap PRIVATE libchibi-common) # # Core library # add_library(libchibi-scheme ${chibi-scheme-srcs}) target_link_libraries(libchibi-scheme PUBLIC libchibi-common) set_target_properties(libchibi-scheme PROPERTIES PREFIX "" # It's liblibchibi-scheme otherwise SOVERSION ${CMAKE_PROJECT_VERSION_MAJOR} VERSION ${CMAKE_PROJECT_VERSION}) # # Generate modules # file(GLOB_RECURSE slds RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS lib/*.sld) if (chibi-scheme-exclude-modules) # CMake doesn't complain anymore about an empty 2nd argument, but 3.12 does. When we require a # more recent version, the if-guard should go. list(REMOVE_ITEM slds ${chibi-scheme-exclude-modules}) endif() set(chibi-ffi ${CMAKE_CURRENT_SOURCE_DIR}/tools/chibi-ffi) set(chibi-genstatic ${CMAKE_CURRENT_SOURCE_DIR}/tools/chibi-genstatic) add_custom_target(chibi-compiled-libs) function(add_compiled_library cfile) if (NOT BUILD_SHARED_LIBS) return() endif() set(link-libraries LINK_LIBRARIES) cmake_parse_arguments(compiledlib-options "" "" "${link-libraries}" ${ARGN}) get_filename_component(basename ${cfile} NAME_WE) get_filename_component(libdir ${cfile} DIRECTORY) if(NOT IS_ABSOLUTE ${libdir}) set(libdir ${CMAKE_CURRENT_BINARY_DIR}/${libdir}) endif() file(RELATIVE_PATH libname ${CMAKE_CURRENT_BINARY_DIR} ${libdir}/${basename}) string(REPLACE "/" "-" libname ${libname}) add_library(${libname} ${cfile}) target_link_libraries(${libname} PRIVATE libchibi-scheme ${compiledlib-options_LINK_LIBRARIES}) add_dependencies(chibi-compiled-libs ${libname}) set_target_properties(${libname} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${libdir} LIBRARY_OUTPUT_NAME ${basename} PREFIX "") file(RELATIVE_PATH installsubdir ${CMAKE_CURRENT_BINARY_DIR}/lib ${libdir}) install(TARGETS ${libname} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/chibi/${installsubdir}) endfunction() if(BUILD_SHARED_LIBS) # This makes sure we only use the separate bootstrap executable for static # builds. With dynamic linking, the default executable is fine. The dispatch # is not a generator expression within the actual custom command to process # the stubs, as older CMake versions fail to properly construct the dependency # on the bootstrap executable from the generator expression. set(bootstrap chibi-scheme) else() set(bootstrap chibi-scheme-bootstrap) endif() function(add_stubs_library stub) set(link-libraries LINK_LIBRARIES) cmake_parse_arguments(stubs-options "" "" "${link-libraries}" ${ARGN}) get_filename_component(stubdir ${stub} PATH) get_filename_component(basename ${stub} NAME_WE) set(stubfile ${CMAKE_CURRENT_SOURCE_DIR}/${stub}) set(stubdir ${CMAKE_CURRENT_BINARY_DIR}/${stubdir}) set(stubout ${stubdir}/${basename}.c) set(stubouts ${stubouts} ${stubout} PARENT_SCOPE) set(stublinkedlibs ${stublinkedlibs} ${stubs-options_LINK_LIBRARIES} PARENT_SCOPE) file(MAKE_DIRECTORY ${stubdir}) add_custom_command(OUTPUT ${stubout} COMMAND ${bootstrap} ${chibi-ffi} ${stubfile} ${stubout} DEPENDS ${stubfile} ${chibi-ffi} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) add_compiled_library(${stubout} LINK_LIBRARIES ${stubs-options_LINK_LIBRARIES}) endfunction() add_stubs_library(lib/chibi/crypto/crypto.stub) add_stubs_library(lib/chibi/emscripten.stub) add_stubs_library(lib/chibi/filesystem.stub) add_stubs_library(lib/chibi/io/io.stub) add_stubs_library(lib/scheme/bytevector.stub) add_stubs_library(lib/srfi/144/math.stub) add_stubs_library(lib/srfi/160/uvprims.stub) if(NOT WIN32) add_stubs_library(lib/chibi/net.stub) add_stubs_library(lib/chibi/process.stub) add_stubs_library(lib/chibi/pty.stub LINK_LIBRARIES util) add_stubs_library(lib/chibi/stty.stub) add_stubs_library(lib/chibi/system.stub) add_stubs_library(lib/chibi/time.stub) else() add_stubs_library(lib/chibi/win32/process-win32.stub) endif() add_custom_target(chibi-scheme-stubs DEPENDS ${stubouts}) if (NOT BUILD_SHARED_LIBS) add_dependencies(libchibi-scheme chibi-scheme-stubs) endif() add_compiled_library(lib/chibi/weak.c) add_compiled_library(lib/chibi/heap-stats.c) add_compiled_library(lib/chibi/disasm.c) add_compiled_library(lib/chibi/ast.c) add_compiled_library(lib/chibi/json.c) add_compiled_library(lib/srfi/18/threads.c) add_compiled_library(lib/chibi/optimize/rest.c) add_compiled_library(lib/chibi/optimize/profile.c) add_compiled_library(lib/srfi/27/rand.c) add_compiled_library(lib/srfi/151/bit.c) add_compiled_library(lib/srfi/39/param.c) add_compiled_library(lib/srfi/69/hash.c) add_compiled_library(lib/srfi/95/qsort.c) add_compiled_library(lib/srfi/98/env.c) add_compiled_library(lib/scheme/time.c) # # Generate clib.c for SEXP_USE_STATIC_LIBS # if (NOT BUILD_SHARED_LIBS) string(REPLACE ";" "\n" genstatic-input "${slds}") set(clibin ${CMAKE_CURRENT_BINARY_DIR}/clib-in.txt) set(clibout ${CMAKE_CURRENT_BINARY_DIR}/clib.c) set(genstatic-helper ${CMAKE_CURRENT_LIST_DIR}/contrib/chibi-genstatic-helper.cmake) file(WRITE ${clibin} "${genstatic-input}") add_custom_command(OUTPUT ${clibout} COMMAND ${CMAKE_COMMAND} -DEXEC=$ -DGENSTATIC=${chibi-genstatic} -DSTUBS=${clibin} -DOUT=${clibout} -P ${genstatic-helper} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS chibi-scheme-bootstrap ${chibi-genstatic} ${genstatic-helper} ${slds}) # The generated file will #include both manually written files in # the source directory as well as files generated by chibi-ffi in # the build directory. The latter can be found without special flags, # as they are relative to the clib.c, but the preprocessor needs # help for the former. As only clib.c needs this flag, we set it # as locally as possible, i.e., not as a target property. set_source_files_properties(${clibout} PROPERTIES INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(libchibi-scheme PUBLIC SEXP_USE_STATIC_LIBS=1) target_sources(libchibi-scheme PRIVATE ${clibout}) target_link_libraries(libchibi-scheme PRIVATE ${stublinkedlibs}) endif() # # Interpreter # add_executable(chibi-scheme main.c) target_link_libraries(chibi-scheme PRIVATE libchibi-scheme) # # Generate "chibi/install.h" # if(WIN32) set(platform "windows") elseif(CYGWIN) set(platform "cygwin") elseif(APPLE) set(platform "macosx") elseif(CMAKE_SYSTEM MATCHES "[Bb][Ss][Dd]") set(platform "bsd") elseif(CMAKE_SYSTEM MATCHES "[Aa]ndroid") set(platform "android") elseif(CMAKE_SYSTEM MATCHES "[Ss]un[Oo][Ss]") set(platform "solaris") elseif (CMAKE_SYSTEM MATCHES "[Ll]inux") set(platform "linux") else() set(platform "unix") endif() if(WIN32) # Leave this empty for now, as the default GNU install directories won't # help on Windows. set(default_module_path "") else() string(JOIN ":" default_module_path ${CMAKE_INSTALL_FULL_DATAROOTDIR}/chibi ${CMAKE_INSTALL_FULL_LIBDIR}/chibi ${CMAKE_INSTALL_FULL_DATAROOTDIR}/snow ${CMAKE_INSTALL_FULL_LIBDIR}/snow) endif() configure_file(include/chibi/install.h.in include/chibi/install.h) # # Testing # enable_testing() set(chibi-scheme-tests r7rs-tests division-tests syntax-tests unicode-tests) foreach(e ${chibi-scheme-tests}) add_test(NAME "${e}" COMMAND chibi-scheme -I ${CMAKE_CURRENT_BINARY_DIR}/lib tests/${e}.scm WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endforeach() add_test(NAME r5rs-test COMMAND chibi-scheme -I ${CMAKE_CURRENT_BINARY_DIR}/lib -xchibi tests/r5rs-tests.scm WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) file(GLOB_RECURSE srfi_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/lib CONFIGURE_DEPENDS lib/srfi/*/test.sld) file(GLOB_RECURSE chibi_scheme_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/lib CONFIGURE_DEPENDS lib/chibi/*-test.sld) set(win32testexcludes # Excluded tests chibi/filesystem-test chibi/memoize-test chibi/term/ansi-test chibi/weak-test # Not ported to Win32 srfi/18/test # Threading chibi/doc-test # Depends (chibi time) chibi/log-test chibi/system-test chibi/tar-test # Depends (chibi system) chibi/process-test # Not applicable chibi/pty-test # Depends (chibi pty) chibi/shell-test # Depends Linux procfs ) foreach(e ${srfi_tests} ${chibi_scheme_tests}) get_filename_component(pth ${e} PATH) get_filename_component(nam ${e} NAME_WE) list(APPEND testlibs ${pth}/${nam}) endforeach() if(WIN32) list(REMOVE_ITEM testlibs ${win32testexcludes}) endif() foreach(e ${testlibs}) string(REGEX REPLACE "/" "_" testname ${e}) string(REGEX REPLACE "/" " " form ${e}) add_test(NAME "lib_${testname}" COMMAND chibi-scheme -I ${CMAKE_CURRENT_BINARY_DIR}/lib -e "(import (${form}))" -e "(run-tests)" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endforeach() # # Testing (embedding) # add_executable(test-foreign-apply-loop tests/foreign/apply-loop.c) target_link_libraries(test-foreign-apply-loop PRIVATE libchibi-scheme) add_test(NAME "foreign-apply-loop" COMMAND test-foreign-apply-loop WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_executable(test-foreign-typeid tests/foreign/typeid.c) target_link_libraries(test-foreign-typeid PRIVATE libchibi-scheme) add_test(NAME "foreign-typeid" COMMAND test-foreign-typeid WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) # # Image, pkgconfig and meta file generation # add_custom_command(OUTPUT chibi.img COMMAND chibi-scheme -I ${CMAKE_CURRENT_BINARY_DIR}/lib -mchibi.repl -d ${CMAKE_CURRENT_BINARY_DIR}/chibi.img WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) add_custom_command(OUTPUT red.img COMMAND chibi-scheme -I ${CMAKE_CURRENT_BINARY_DIR}/lib -xscheme.red -mchibi.repl -d ${CMAKE_CURRENT_BINARY_DIR}/red.img WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) add_custom_command(OUTPUT snow.img COMMAND chibi-scheme -I ${CMAKE_CURRENT_BINARY_DIR}/lib -mchibi.snow.commands -mchibi.snow.interface -mchibi.snow.package -mchibi.snow.utils -d ${CMAKE_CURRENT_BINARY_DIR}/snow.img WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) if(BUILD_SHARED_LIBS) # Currently, image dumps only work with shared library builds, which includes Windows add_custom_target(chibi-images ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/chibi.img ${CMAKE_CURRENT_BINARY_DIR}/red.img ${CMAKE_CURRENT_BINARY_DIR}/snow.img # The dependency on libchibi-scheme is crucial here: chibi-compiled-libs) endif() configure_file(contrib/chibi-scheme.pc.cmake.in chibi-scheme.pc @ONLY) function(generate_package_list libdir output) add_custom_command(OUTPUT ${output} COMMAND ${CMAKE_COMMAND} -DEXEC=$ -DLIBDIR=${libdir} -DGENMETA=tools/generate-install-meta.scm -DVERSION=${CMAKE_PROJECT_VERSION} -DOUT=${CMAKE_CURRENT_BINARY_DIR}/${output} -P contrib/chibi-generate-install-meta-helper.cmake WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS chibi-scheme tools/generate-install-meta.scm contrib/chibi-generate-install-meta-helper.cmake) endfunction() generate_package_list(lib/chibi .chibi.meta) generate_package_list(lib/scheme .scheme.meta) generate_package_list(lib/srfi .srfi.meta) add_custom_target(chibi-meta-lists ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/.chibi.meta ${CMAKE_CURRENT_BINARY_DIR}/.scheme.meta ${CMAKE_CURRENT_BINARY_DIR}/.srfi.meta) # # Installation # install(DIRECTORY include/chibi DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} PATTERN "sexp-*.[hc]" EXCLUDE PATTERN "*.h.in" EXCLUDE) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/chibi/install.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/chibi) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/chibi-scheme.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) add_library(chibi::libchibi-scheme ALIAS libchibi-scheme) install(TARGETS libchibi-scheme libchibi-common chibi-scheme EXPORT chibi-scheme-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(FILES tools/chibi-ffi tools/chibi-doc tools/snow-chibi tools/snow-chibi.scm DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES doc/chibi-scheme.1 doc/chibi-ffi.1 doc/chibi-doc.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) if(BUILD_SHARED_LIBS) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/chibi.img ${CMAKE_CURRENT_BINARY_DIR}/red.img ${CMAKE_CURRENT_BINARY_DIR}/snow.img DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/chibi) endif() install(DIRECTORY lib/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/chibi PATTERN "*win32" EXCLUDE PATTERN "*test.sld" EXCLUDE PATTERN "*.c" EXCLUDE PATTERN "*.stub" EXCLUDE) # This is to revert the above exclusion pattern install(FILES lib/chibi/test.sld DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/chibi/chibi) if(WIN32) install(DIRECTORY lib/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/chibi FILES_MATCHING PATTERN "*win32/*.scm" PATTERN "*win32/*.sld") endif() install(FILES ${CMAKE_CURRENT_BINARY_DIR}/.chibi.meta ${CMAKE_CURRENT_BINARY_DIR}/.scheme.meta ${CMAKE_CURRENT_BINARY_DIR}/.srfi.meta DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/chibi) install(EXPORT chibi-scheme-targets FILE chibi-scheme-targets.cmake NAMESPACE chibi:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/chibi) write_basic_package_version_file(chibi-scheme-config-version.cmake VERSION ${CMAKE_PROJECT_VERSION} COMPATIBILITY ExactVersion) install(FILES contrib/chibi-scheme-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/chibi-scheme-config-version.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/chibi)