cmake_minimum_required(VERSION 3.16) # std::filesystem needs macOS 10.15 or newer. Has to be set before project(). if(APPLE AND NOT CMAKE_OSX_DEPLOYMENT_TARGET) set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum macOS version") endif() project(antlrlab VERSION 1.0.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) endif() # The ANTLR tool (Java) and the C++ runtime must be the same version, because # the serialized grammar that the tool writes is read by the runtime. set(ANTLR_VERSION "4.13.2" CACHE STRING "ANTLR version to build against") option(ANTLRLAB_USE_SYSTEM_RUNTIME "Use an installed ANTLR C++ runtime instead of downloading one" OFF) option(ANTLRLAB_DOWNLOAD_JAR "Download antlr-${ANTLR_VERSION}-complete.jar at configure time and use it by default" OFF) if(MSVC) option(ANTLRLAB_STATIC_CRT "Visual C++: link the C++ runtime statically, so the program needs no redistributable" ON) elseif(MINGW) option(ANTLRLAB_STATIC_LINK "MinGW: link libgcc, libstdc++ and winpthread statically, so no DLLs have to ship with the program" ON) endif() find_package(Threads REQUIRED) # Visual C++: the runtime and antlrlab have to use the same C runtime library. if(MSVC) if(ANTLRLAB_STATIC_CRT) set(WITH_STATIC_CRT ON CACHE BOOL "" FORCE) # read by the ANTLR runtime's own CMakeLists set(ANTLRLAB_MSVC_RUNTIME "MultiThreaded$<$:Debug>") else() set(WITH_STATIC_CRT OFF CACHE BOOL "" FORCE) set(ANTLRLAB_MSVC_RUNTIME "MultiThreaded$<$:Debug>DLL") endif() endif() # --------------------------------------------------------------- runtime ---- if(ANTLRLAB_USE_SYSTEM_RUNTIME) find_path(ANTLR4_INCLUDE_DIR antlr4-runtime.h PATH_SUFFIXES antlr4-runtime include/antlr4-runtime) find_library(ANTLR4_LIBRARY NAMES antlr4-runtime antlr4-runtime-static) if(NOT ANTLR4_INCLUDE_DIR OR NOT ANTLR4_LIBRARY) message(FATAL_ERROR "ANTLR C++ runtime not found. Install it, set ANTLR4_INCLUDE_DIR and " "ANTLR4_LIBRARY, or configure without -DANTLRLAB_USE_SYSTEM_RUNTIME=ON.") endif() set(ANTLRLAB_RUNTIME_LIBS ${ANTLR4_LIBRARY}) set(ANTLRLAB_RUNTIME_INCLUDES ${ANTLR4_INCLUDE_DIR}) set(ANTLRLAB_RUNTIME_DEFS "") else() include(FetchContent) set(ANTLR_BUILD_CPP_TESTS OFF CACHE BOOL "" FORCE) set(ANTLR_BUILD_SHARED OFF CACHE BOOL "" FORCE) set(ANTLR_BUILD_STATIC ON CACHE BOOL "" FORCE) set(WITH_DEMO OFF CACHE BOOL "" FORCE) set(ANTLR4_INSTALL OFF CACHE BOOL "" FORCE) set(_antlr_fetch_args URL "https://github.com/antlr/antlr4/archive/refs/tags/${ANTLR_VERSION}.tar.gz" SOURCE_SUBDIR runtime/Cpp) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) list(APPEND _antlr_fetch_args DOWNLOAD_EXTRACT_TIMESTAMP TRUE) endif() FetchContent_Declare(antlr4 ${_antlr_fetch_args}) FetchContent_MakeAvailable(antlr4) # The runtime forgets some standard #includes ( for one), which newer # standard libraries, notably Visual C++'s, no longer paper over. Supply them. if(TARGET antlr4_static) set(_compat "${CMAKE_CURRENT_SOURCE_DIR}/cmake/antlr_runtime_compat.h") if(MSVC) target_compile_options(antlr4_static PRIVATE "/FI${_compat}") else() target_compile_options(antlr4_static PRIVATE -include "${_compat}") endif() endif() set(ANTLRLAB_RUNTIME_LIBS antlr4_static) set(ANTLRLAB_RUNTIME_INCLUDES "${antlr4_SOURCE_DIR}/runtime/Cpp/runtime/src") set(ANTLRLAB_RUNTIME_DEFS ANTLR4CPP_STATIC) endif() # ------------------------------------------------------------------- jar ---- set(ANTLRLAB_JAR_PATH "") if(ANTLRLAB_DOWNLOAD_JAR) set(_jar "${CMAKE_BINARY_DIR}/antlr-${ANTLR_VERSION}-complete.jar") if(NOT EXISTS "${_jar}") # antlr.org first, then the copy in the ANTLR website's GitHub repository. foreach(_url "https://www.antlr.org/download/antlr-${ANTLR_VERSION}-complete.jar" "https://raw.githubusercontent.com/antlr/website-antlr4/gh-pages/download/antlr-${ANTLR_VERSION}-complete.jar") message(STATUS "Downloading ${_url}") file(DOWNLOAD "${_url}" "${_jar}" STATUS _dl_status SHOW_PROGRESS) list(GET _dl_status 0 _dl_code) if(_dl_code EQUAL 0) break() endif() file(REMOVE "${_jar}") message(STATUS " failed: ${_dl_status}") endforeach() if(NOT EXISTS "${_jar}") message(WARNING "Could not download the ANTLR jar. " "Pass --antlr-jar or set ANTLR_JAR when running antlrlab.") endif() endif() if(EXISTS "${_jar}") file(TO_CMAKE_PATH "${_jar}" ANTLRLAB_JAR_PATH) endif() endif() # ---------------------------------------------------------------- target ---- add_executable(antlrlab src/main.cpp) target_include_directories(antlrlab PRIVATE src ${ANTLRLAB_RUNTIME_INCLUDES}) target_link_libraries(antlrlab PRIVATE ${ANTLRLAB_RUNTIME_LIBS} Threads::Threads) target_compile_definitions(antlrlab PRIVATE ${ANTLRLAB_RUNTIME_DEFS} LAB_VERSION="${PROJECT_VERSION}" LAB_ANTLR_VERSION="${ANTLR_VERSION}") if(ANTLRLAB_JAR_PATH) target_compile_definitions(antlrlab PRIVATE LAB_DEFAULT_ANTLR_JAR="${ANTLRLAB_JAR_PATH}") # Also put the jar next to the program: antlrlab looks there too, which keeps # working when the build directory is moved, or its path has unusual characters. add_custom_command(TARGET antlrlab POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${ANTLRLAB_JAR_PATH}" "$") install(FILES "${ANTLRLAB_JAR_PATH}" DESTINATION bin) endif() # std::filesystem lives in a separate library in GCC 8 and older, and in Clang # with libc++ 8 and older. include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_FLAGS "-std=c++17") if(MSVC) set(CMAKE_REQUIRED_FLAGS "/std:c++17") endif() check_cxx_source_compiles("#include int main() { return std::filesystem::current_path().empty() ? 1 : 0; }" ANTLRLAB_FS_BUILTIN) unset(CMAKE_REQUIRED_FLAGS) if(NOT ANTLRLAB_FS_BUILTIN) foreach(_fslib stdc++fs c++fs) set(CMAKE_REQUIRED_LIBRARIES ${_fslib}) check_cxx_source_compiles("#include int main() { return std::filesystem::current_path().empty() ? 1 : 0; }" ANTLRLAB_FS_${_fslib}) unset(CMAKE_REQUIRED_LIBRARIES) if(ANTLRLAB_FS_${_fslib}) target_link_libraries(antlrlab PRIVATE ${_fslib}) break() endif() endforeach() endif() if(WIN32) # Winsock (web UI), BCrypt (access token), Shell32 (command line, opening the # browser), DbgHelp (stack traces in crash reports) target_link_libraries(antlrlab PRIVATE ws2_32 bcrypt shell32 dbghelp) target_compile_definitions(antlrlab PRIVATE _WIN32_WINNT=0x0601 NOMINMAX WIN32_LEAN_AND_MEAN) endif() # Crash reports print a stack trace; ENABLE_EXPORTS (-rdynamic) makes it show function names. if(NOT WIN32) set_target_properties(antlrlab PROPERTIES ENABLE_EXPORTS ON) endif() # The ANTLR runtime recurses deeply on real grammars. antlrlab runs it on a # thread with a big stack it creates itself (src/isolate.hpp), so the main # program only needs a normal stack; 8 MB matches the Linux default for the # threads of the web server. if(MSVC) target_link_options(antlrlab PRIVATE /STACK:8388608) set_property(TARGET antlrlab PROPERTY MSVC_RUNTIME_LIBRARY "${ANTLRLAB_MSVC_RUNTIME}") target_compile_options(antlrlab PRIVATE /utf-8 /EHsc /permissive- /W3) target_compile_definitions(antlrlab PRIVATE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS) else() target_compile_options(antlrlab PRIVATE -Wall -Wextra) if(MINGW) target_link_options(antlrlab PRIVATE -Wl,--stack,8388608) if(ANTLRLAB_STATIC_LINK) target_link_options(antlrlab PRIVATE -static -static-libgcc -static-libstdc++) endif() endif() endif() install(TARGETS antlrlab RUNTIME DESTINATION bin)