Files
AntlrLab/CMakeLists.txt
T
2026-10-07 18:11:55 -03:00

192 lines
7.8 KiB
CMake

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$<$<CONFIG:Debug>:Debug>")
else()
set(WITH_STATIC_CRT OFF CACHE BOOL "" FORCE)
set(ANTLRLAB_MSVC_RUNTIME "MultiThreaded$<$<CONFIG:Debug>: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 (<chrono> 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}" "$<TARGET_FILE_DIR:antlrlab>")
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 <filesystem>
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 <filesystem>
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)