diff --git a/CMakeLists.txt b/CMakeLists.txt index c02b5b82..d45416eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,3 @@ -# -# FIXME: This CMakeLists.txt is only for Win32 platforms for now -# cmake_minimum_required(VERSION 3.12) @@ -14,25 +11,26 @@ project(chibi-scheme LANGUAGES C VERSION ${version} DESCRIPTION "Chibi-Scheme: minimal r7rs implementation, release: ${release}") include(CheckIncludeFile) - -if(APPLE) - message(FATAL_ERROR - "DYLD platforms are not supported with this CMakeLists.txt. Use Makefile instead.") -endif() - -if(UNIX) - message(FATAL_ERROR - "UNIX platforms are not supported with this CMakeLists.txt. Use Makefile instead.") -endif() +include(CheckSymbolExists) +include(GNUInstallDirs) 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_include_file(stdint.h HAVE_STDINT_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) @@ -41,6 +39,12 @@ else() 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) @@ -56,40 +60,43 @@ endif() # # Default settings for all targets. We use an interface library here to not -# pollute/mutate global settings. +# pollute/mutate global settings. Any configuration applied to this library +# is propagated to its client targets. # add_library(libchibi-common INTERFACE) -if (NOT BUILD_SHARED_LIBS) - target_compile_definitions(libchibi-common INTERFACE SEXP_STATIC_LIBRARY=1) -endif() +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} $ $ $) -if (NOT BUILD_SHARED_LIBS) - target_compile_definitions(libchibi-common INTERFACE SEXP_USE_DL=0) -else() - target_compile_definitions(libchibi-common INTERFACE SEXP_USE_DL=1) -endif() - -if(HAVE_STDINT_H) - target_compile_definitions(libchibi-common INTERFACE SEXP_USE_INTTYPES) -endif() - -if(HAVE_NTP_GETTIME) - target_compile_definitions(libchibi-common INTERFACE SEXP_USE_NTPGETTIME) -endif() - -if(NOT HAVE_POLL_H) - # Disable green threads: It depends on non-blocking I/O - target_compile_definitions(libchibi-common INTERFACE SEXP_USE_GREEN_THREADS=0) -endif() +target_link_libraries(libchibi-common INTERFACE + ${BOEHMGC} + $<$:-fsanitize=address,undefined> + $<$:ws2_32>) # # Sources @@ -117,13 +124,7 @@ add_executable(chibi-scheme-bootstrap ${chibi-scheme-srcs} main.c) -if(WIN32) - target_link_libraries(chibi-scheme-bootstrap - PRIVATE ws2_32 libchibi-common) -else() - target_link_libraries(chibi-scheme-bootstrap - PRIVATE libchibi-common) -endif() +target_link_libraries(chibi-scheme-bootstrap PRIVATE libchibi-common) # @@ -138,6 +139,7 @@ target_link_libraries(libchibi-scheme set_target_properties(libchibi-scheme PROPERTIES + PREFIX "" # It's liblibchibi-scheme otherwise SOVERSION ${CMAKE_PROJECT_VERSION_MAJOR} VERSION ${CMAKE_PROJECT_VERSION}) @@ -145,39 +147,112 @@ set_target_properties(libchibi-scheme # # Generate modules # -# FIXME: Currently, it depends on GLOB thus we have to re-run CMake -# when we've gotten additional/removed library -file(GLOB_RECURSE stubs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/lib - ${CMAKE_CURRENT_SOURCE_DIR}/lib/*.stub) file(GLOB_RECURSE slds RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/lib/*.sld) -list(REMOVE_ITEM slds ${chibi-scheme-exclude-modules}) + 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) -set(stuboutdir ${CMAKE_CURRENT_BINARY_DIR}/stubs/lib) -foreach(e ${stubs}) - get_filename_component(stubdir ${e} PATH) - get_filename_component(basename ${e} NAME_WE) - set(stubfile ${CMAKE_CURRENT_SOURCE_DIR}/lib/${e}) - set(stubdir ${stuboutdir}/${stubdir}) +function(add_compiled_library cfile) + if (NOT BUILD_SHARED_LIBS) + return() + endif() + + 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) + + set_target_properties(${libname} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${libdir} + LIBRARY_OUTPUT_NAME ${basename} + PREFIX "") +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) + 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) + file(MAKE_DIRECTORY ${stubdir}) + + add_custom_command(OUTPUT ${stubout} - COMMAND chibi-scheme-bootstrap - ${chibi-ffi} ${stubfile} ${stubout} + COMMAND ${bootstrap} ${chibi-ffi} ${stubfile} ${stubout} DEPENDS ${stubfile} ${chibi-ffi} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - list(APPEND stubouts ${stubout}) -endforeach() + + add_compiled_library(${stubout}) +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) + 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_dependencies(libchibi-scheme chibi-scheme-stubs) +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 @@ -206,30 +281,26 @@ if (NOT BUILD_SHARED_LIBS) ${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 - PUBLIC + PRIVATE ${clibout}) - - target_include_directories(libchibi-common - INTERFACE - $ - $) -elseif(WIN32) - target_link_libraries(libchibi-scheme ws2_32) - target_compile_definitions(libchibi-scheme PUBLIC BUILDING_DLL=1) endif() -function(bless_chibi_scheme_executable tgt) - target_link_libraries(${tgt} libchibi-scheme) - if(WIN32 AND NOT ${BUILD_SHARED_LIBS}) - target_link_libraries(${tgt} ws2_32) - endif() -endfunction() - # # Interpreter # @@ -237,35 +308,42 @@ endfunction() add_executable(chibi-scheme main.c) -bless_chibi_scheme_executable(chibi-scheme) +target_link_libraries(chibi-scheme + PRIVATE libchibi-scheme) # # Generate "chibi/install.h" # -if(CYGWIN OR WIN32) - set(thePrefix "bin") -else() - set(thePrefix "lib") -endif() - -if(WIN32) - set(pathsep "\\;") -else() - set(pathsep ":") -endif() - 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 "unknown") + set(platform "unix") endif() - -set(default_module_path - "" - #"${CMAKE_INSTALL_PREFIX}/${thePrefix}${pathsep}${CMAKE_INSTALL_PREFIX}/bin" - ) +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) @@ -277,28 +355,27 @@ enable_testing() set(chibi-scheme-tests r7rs-tests - ## Not connected - #division-tests - #r5rs-tests - #syntax-tests - #unicode-tests - ## Require threads - # lib-tests - ) + division-tests + syntax-tests + unicode-tests) foreach(e ${chibi-scheme-tests}) add_test(NAME "${e}" - COMMAND chibi-scheme tests/${e}.scm + 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 - ${CMAKE_CURRENT_SOURCE_DIR}/lib/srfi/*/test.sld) + CONFIGURE_DEPENDS lib/srfi/*/test.sld) file(GLOB_RECURSE chibi_scheme_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/lib - ${CMAKE_CURRENT_SOURCE_DIR}/lib/chibi/*-test.sld) + CONFIGURE_DEPENDS lib/chibi/*-test.sld) -set(testexcludes +set(win32testexcludes # Excluded tests chibi/filesystem-test chibi/memoize-test @@ -315,19 +392,22 @@ set(testexcludes chibi/pty-test # Depends (chibi pty) ) -set(testlibs) 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() -list(REMOVE_ITEM testlibs ${testexcludes}) + +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 -e "(import (${form}))" + COMMAND chibi-scheme -I ${CMAKE_CURRENT_BINARY_DIR}/lib + -e "(import (${form}))" -e "(run-tests)" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endforeach() @@ -339,7 +419,8 @@ endforeach() add_executable(test-foreign-apply-loop tests/foreign/apply-loop.c) -bless_chibi_scheme_executable(test-foreign-apply-loop) +target_link_libraries(test-foreign-apply-loop + PRIVATE libchibi-scheme) add_test(NAME "foreign-apply-loop" COMMAND test-foreign-apply-loop @@ -348,8 +429,28 @@ add_test(NAME "foreign-apply-loop" add_executable(test-foreign-typeid tests/foreign/typeid.c) -bless_chibi_scheme_executable(test-foreign-typeid) +target_link_libraries(test-foreign-typeid + PRIVATE libchibi-scheme) add_test(NAME "foreign-typeid" COMMAND test-foreign-typeid WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +# +# Installation +# + +install(DIRECTORY include/chibi + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + USE_SOURCE_PERMISSIONS + PATTERN "sexp-*.[hc]" EXCLUDE + PATTERN "*.h.in" EXCLUDE) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/chibi/install.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/chibi) + +add_library(chibi::libchibi-scheme ALIAS libchibi-scheme) + +install(TARGETS libchibi-scheme + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})