cmake_minimum_required(VERSION 3.15...4.1)
#### Check important configuration pre-requisites.
#
# Discourage in-source builds; they overwrite the hand-written Makefile, and generally cause confusion.
# Use `cmake . -B
` or the CMake GUI to do an out-of-source build.
if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
message(FATAL_ERROR "Out-of-source builds are recommended for this project.")
endif()
#### Extract version from include/libjsonnet.h so it can be put in the project version.
#
# Extract release version number from include/libjsonnet.h.
# We do this in various other (non-CMake-build) places too, e.g., the Python wheel build.
set(JSONNET_HEADER_VERSION_REGEX
[=[^[ \t]*#[ \t]*define[ \t]+LIB_JSONNET_VERSION[ \t]+\"v([0-9.]+(-?[a-z][a-z0-9]*)?)\"[ \t]*$]=])
file(
STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/include/libjsonnet.h JSONNET_HEADER_VERSION
LIMIT_COUNT 1
REGEX ${JSONNET_HEADER_VERSION_REGEX})
string(REGEX REPLACE ${JSONNET_HEADER_VERSION_REGEX} "\\1" JSONNET_HEADER_VERSION "${JSONNET_HEADER_VERSION}")
message(VERBOSE "Extracted version from include/libjsonnet.h: ${JSONNET_HEADER_VERSION}")
#### Set CMake project.
#
project(
jsonnet
VERSION ${JSONNET_HEADER_VERSION}
HOMEPAGE_URL "https://jsonnet.org/"
LANGUAGES C CXX)
#### Unconditional CMake module includes.
#
include(GNUInstallDirs)
#### Set up cacheable/user-configurable state.
#
# User-configurable options.
option(BUILD_JSONNET "Build jsonnet command-line tool." ON)
option(BUILD_JSONNETFMT "Build jsonnetfmt command-line tool." ON)
option(BUILD_TESTS "Build and run jsonnet tests." ON)
option(BUILD_STATIC_LIBS "Build a static libjsonnet." ON)
option(BUILD_SHARED_LIBS "Build shared libjsonnet." ON)
option(BUILD_SHARED_BINARIES "Link binaries to the shared libjsonnet instead of the static one." OFF)
option(BUILD_MAN_PAGES "Build manpages." ON)
option(USE_SYSTEM_GTEST "Use system-provided gtest library" OFF)
option(USE_SYSTEM_JSON "Use the system-provided json library" OFF)
# TODO: Support using a system Rapid YAML install.
#### Compute derived values from user-configurable state.
#
# Helper variable set to ON if there is any reason we need to build the jsonnet command binaries.
if(BUILD_TESTS OR BUILD_JSONNET OR BUILD_JSONNETFMT)
set(BUILD_SOME_BINARIES ON)
else()
set(BUILD_SOME_BINARIES OFF)
endif()
#### System package searches.
#
if(USE_SYSTEM_JSON)
find_package(nlohmann_json 3.6.1 REQUIRED)
endif()
#### Utility function to configure a target with our preferred C and C++ compilation flags.
#
function(configure_target_cc_props LIB_TARGET_NAME)
set_property(TARGET ${LIB_TARGET_NAME} PROPERTY CXX_STANDARD 17)
set_property(TARGET ${LIB_TARGET_NAME} PROPERTY CXX_EXTENSIONS OFF)
set_property(TARGET ${LIB_TARGET_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
set_property(TARGET ${LIB_TARGET_NAME} PROPERTY C_STANDARD 99)
set_property(TARGET ${LIB_TARGET_NAME} PROPERTY C_EXTENSIONS OFF)
set_property(TARGET ${LIB_TARGET_NAME} PROPERTY C_STANDARD_REQUIRED ON)
if (NOT MSVC)
target_compile_options(${LIB_TARGET_NAME}
PRIVATE
-Wall -Wextra -Wimplicit-fallthrough
-pedantic
$<$:-Woverloaded-virtual>
)
endif()
endfunction()
#### Sub-directory CMakeLists includes.
#
add_subdirectory(stdlib)
#### Utility function to set up all the properties for an OBJECT library for the jsonnet core.
#
# This is done as a function because there separate object libraries for shared vs. static.
# Shared has slightly different compilation flags (it needs -fPIC).
#
function(configure_jsonnet_obj_target LIB_TARGET_NAME)
configure_target_cc_props(${LIB_TARGET_NAME})
target_sources(${LIB_TARGET_NAME}
PRIVATE
third_party/md5/md5.h
third_party/md5/md5.cpp
third_party/rapidyaml/rapidyaml-0.10.0.hpp
third_party/rapidyaml/rapidyaml.cpp
core/static_error.h
core/desugarer.h
core/unicode.h
core/vm.h
core/static_analysis.h
core/path_utils.h
core/state.h
core/pass.h
core/ast.h
core/json.h
core/string_utils.h
core/formatter.h
core/parser.h
core/lexer.h
core/desugarer.cpp
core/formatter.cpp
core/lexer.cpp
core/libjsonnet.cpp
core/parser.cpp
core/pass.cpp
core/path_utils.cpp
core/static_analysis.cpp
core/string_utils.cpp
core/vm.cpp
PUBLIC
include/libjsonnet.h
include/libjsonnet_fmt.h
)
target_link_libraries(${LIB_TARGET_NAME} PRIVATE stdlib_h)
if(USE_SYSTEM_JSON)
target_link_libraries(${LIB_TARGET_NAME} PRIVATE nlohmann_json::nlohmann_json)
else()
target_sources(${LIB_TARGET_NAME}
PRIVATE
third_party/json/nlohmann/json.hpp
)
target_include_directories(${LIB_TARGET_NAME}
SYSTEM PRIVATE third_party/json)
endif()
target_include_directories(${LIB_TARGET_NAME}
PUBLIC
$
$
PRIVATE
third_party/md5
third_party/rapidyaml
)
endfunction()
#### Utility function to set up library target properties for the C lib.
#
function(configure_jsonnet_lib_target LIB_TARGET_NAME OBJ_TARGET_NAME)
configure_target_cc_props(${LIB_TARGET_NAME})
target_link_libraries(${LIB_TARGET_NAME} PUBLIC ${OBJ_TARGET_NAME})
set(JSONNET_PUBLIC_HEADERS include/libjsonnet.h include/libjsonnet_fmt.h)
set_target_properties(${LIB_TARGET_NAME} PROPERTIES
OUTPUT_NAME jsonnet
PUBLIC_HEADER "${JSONNET_PUBLIC_HEADERS}")
endfunction()
#### Utility function to set up library target properties for the C++ lib.
#
function(configure_jsonnet_cpp_lib_target LIB_TARGET_NAME OBJ_TARGET_NAME)
configure_target_cc_props(${LIB_TARGET_NAME})
target_sources(${LIB_TARGET_NAME}
PRIVATE
cpp/libjsonnet++.cpp
include/libjsonnet++.h
)
target_link_libraries(${LIB_TARGET_NAME} PUBLIC ${OBJ_TARGET_NAME})
set(JSONNET_PUBLIC_HEADERS include/libjsonnet.h include/libjsonnet++.h)
set_target_properties(${LIB_TARGET_NAME} PROPERTIES
OUTPUT_NAME jsonnet++
PUBLIC_HEADER "${JSONNET_PUBLIC_HEADERS}")
endfunction()
#### Static library target setup.
#
if(BUILD_STATIC_LIBS OR (BUILD_SOME_BINARIES AND NOT BUILD_SHARED_BINARIES))
add_library(jsonnet_obj_static OBJECT)
configure_jsonnet_obj_target(jsonnet_obj_static)
add_library(jsonnet_static STATIC)
configure_jsonnet_lib_target(jsonnet_static jsonnet_obj_static)
add_library(jsonnet_cpp_static STATIC)
configure_jsonnet_cpp_lib_target(jsonnet_cpp_static jsonnet_obj_static)
if(BUILD_STATIC_LIBS)
install(TARGETS jsonnet_static jsonnet_cpp_static)
endif()
endif()
#### Shared library target setup.
#
if(BUILD_SHARED_LIBS OR (BUILD_SOME_BINARIES AND BUILD_SHARED_BINARIES))
add_library(jsonnet_obj_shared OBJECT)
set_property(TARGET jsonnet_obj_shared PROPERTY POSITION_INDEPENDENT_CODE ON)
configure_jsonnet_obj_target(jsonnet_obj_shared)
add_library(jsonnet_shared SHARED)
configure_jsonnet_lib_target(jsonnet_shared jsonnet_obj_shared)
set_target_properties(jsonnet_shared PROPERTIES
VERSION "${JSONNET_HEADER_VERSION}"
SOVERSION "0")
add_library(jsonnet_cpp_shared SHARED)
configure_jsonnet_cpp_lib_target(jsonnet_cpp_shared jsonnet_obj_shared)
set_target_properties(jsonnet_cpp_shared PROPERTIES
VERSION "${JSONNET_HEADER_VERSION}"
SOVERSION "0")
if(BUILD_SHARED_LIBS)
install(TARGETS jsonnet_shared jsonnet_cpp_shared)
endif()
endif()
#### Alias the library to use for binaries.
#
if(BUILD_SHARED_BINARIES)
add_library(jsonnet_lib_for_binaries ALIAS jsonnet_shared)
add_library(jsonnet_cpp_lib_for_binaries ALIAS jsonnet_cpp_shared)
else()
add_library(jsonnet_lib_for_binaries ALIAS jsonnet_static)
add_library(jsonnet_cpp_lib_for_binaries ALIAS jsonnet_cpp_static)
endif()
#### An object library for the utils.cpp code which is shared by both CLI binaries.
#
if(BUILD_SOME_BINARIES)
add_library(jsonnet_cmd_utils OBJECT
cmd/utils.cpp
cmd/utils.h
)
endif()
#### CLI (jsonnet and jsonnetfmt) binary targets.
#
if(BUILD_JSONNET OR BUILD_TESTS)
add_executable(jsonnet
cmd/jsonnet.cpp
$
)
target_link_libraries(jsonnet PRIVATE jsonnet_lib_for_binaries)
if(BUILD_JSONNET)
install(TARGETS jsonnet)
endif()
endif()
if(BUILD_JSONNETFMT OR BUILD_TESTS)
add_executable(jsonnetfmt
cmd/jsonnetfmt.cpp
$
)
target_link_libraries(jsonnetfmt PRIVATE jsonnet_lib_for_binaries)
if(BUILD_JSONNETFMT)
install(TARGETS jsonnetfmt)
endif()
endif()
#### GoogleTest based tests.
#
if(BUILD_TESTS)
enable_testing()
include(GoogleTest)
if(USE_SYSTEM_GTEST)
find_package(GTest REQUIRED)
else()
include(FetchContent)
FetchContent_Declare(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.17.0
)
# For Windows: Prevent overriding the parent project's compiler/linker settings.
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Prevent including GoogleTest in the generated install rules.
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
endif()
function(jsonnet_add_gtest TESTNAME JSONNET_LIB_TARGET)
add_executable(${TESTNAME} ${ARGN})
target_link_libraries(${TESTNAME} ${JSONNET_LIB_TARGET} gtest gmock gtest_main)
gtest_discover_tests(${TESTNAME}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
PROPERTIES
VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
ENVIRONMENT "JSONNET_SOURCE_BASE=${PROJECT_SOURCE_DIR}"
)
set_target_properties(${TESTNAME} PROPERTIES FOLDER tests)
endfunction()
jsonnet_add_gtest(test_core_unicode jsonnet_lib_for_binaries core/unicode_test.cpp)
jsonnet_add_gtest(test_core_lexer jsonnet_lib_for_binaries core/lexer_test.cpp)
jsonnet_add_gtest(test_core_parser jsonnet_lib_for_binaries core/parser_test.cpp)
jsonnet_add_gtest(test_core_libjsonnet jsonnet_lib_for_binaries core/libjsonnet_test.cpp)
jsonnet_add_gtest(test_cpp_libjsonnet jsonnet_cpp_lib_for_binaries cpp/libjsonnet++_test.cpp)
jsonnet_add_gtest(test_cpp_libjsonnet_locale jsonnet_cpp_lib_for_binaries cpp/libjsonnet_test_locale.cpp)
add_subdirectory(test_suite)
endif() # if(BUILD_TESTS)
#### Man pages
#
if(BUILD_MAN_PAGES AND (BUILD_JSONNET OR BUILD_JSONNETFMT))
find_program(HELP2MAN_BINARY NAMES help2man)
if (HELP2MAN_BINARY)
message(STATUS "help2man found, man pages will be generated.")
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/man/man1")
if(BUILD_JSONNET)
add_custom_command(
OUTPUT "${PROJECT_BINARY_DIR}/man/man1/jsonnet.1"
COMMAND "${HELP2MAN_BINARY}"
ARGS --section=1 --no-info --name="Jsonnet data templating language interpreter" --output="${PROJECT_BINARY_DIR}/man/man1/jsonnet.1" "$"
DEPENDS jsonnet
)
endif()
if(BUILD_JSONNETFMT)
add_custom_command(
OUTPUT "${PROJECT_BINARY_DIR}/man/man1/jsonnetfmt.1"
COMMAND "${HELP2MAN_BINARY}"
ARGS --section=1 --no-info --name="Jsonnet data templating language auto-formatter" --output="${PROJECT_BINARY_DIR}/man/man1/jsonnetfmt.1" "$"
DEPENDS jsonnetfmt
)
endif()
add_custom_target(jsonnet-man ALL DEPENDS "${PROJECT_BINARY_DIR}/man/man1/jsonnet.1")
add_custom_target(jsonnetfmt-man ALL DEPENDS "${PROJECT_BINARY_DIR}/man/man1/jsonnetfmt.1")
install(FILES ${PROJECT_BINARY_DIR}/man/man1/jsonnet.1 DESTINATION "${CMAKE_INSTALL_MANDIR}/man1")
install(FILES ${PROJECT_BINARY_DIR}/man/man1/jsonnetfmt.1 DESTINATION "${CMAKE_INSTALL_MANDIR}/man1")
else()
message(STATUS "help2man not found, man pages will not be generated.")
endif()
endif() # if(BUILD_MAN_PAGES)
#### CMake debugging outputs
#
# Note cmake_language GET_MESSAGE_LOG_LEVEL exists only in 3.25 and above.
if(CMAKE_VERSION VERSION_GREATER "3.25")
include(CMakePrintHelpers)
cmake_language(GET_MESSAGE_LOG_LEVEL CURRENT_CMAKE_LOG_LEVEL)
if(CURRENT_CMAKE_LOG_LEVEL MATCHES "TRACE")
# Print specific properties for specific targets
cmake_print_properties(
TARGETS
to_c_array
stdlib_h
jsonnet_obj_static
jsonnet_obj_shared
jsonnet_static
jsonnet_shared
jsonnet_cpp_static
jsonnet_cpp_shared
jsonnet_cmd_utils
jsonnet
jsonnetfmt
jsonnet-man
jsonnetfmt-man
PROPERTIES
TYPE
C_STANDARD
CXX_STANDARD
POSITION_INDEPENDENT_CODE
PUBLIC_LINK_DEPENDS
INTERFACE_LINK_DEPENDS
PUBLIC_LINK_LIBRARIES
INTERFACE_LINK_LIBRARIES
PUBLIC_INCLUDE_DIRECTORIES
INTERFACE_INCLUDE_DIRECTORIES
OUTPUT_NAME
PUBLIC_HEADER
RUNTIME_OUTPUT_DIRECTORY
)
endif()
endif()