Add CMake support.

This commit is contained in:
Jason Kankiewicz 2022-02-10 23:28:54 -08:00 committed by Jason Kankiewicz
parent 7bc3bb6850
commit fb62c4b02a
5 changed files with 441 additions and 0 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@
/.direnv
perf.*
/Cargo.lock
build/

132
automerge-c/CMakeLists.txt Normal file
View file

@ -0,0 +1,132 @@
cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# Parse the library name, project name and project version out of Cargo's TOML file.
set(CARGO_LIB_SECTION OFF)
set(LIBRARY_NAME "")
set(CARGO_PKG_SECTION OFF)
set(CARGO_PKG_NAME "")
set(CARGO_PKG_VERSION "")
file(READ Cargo.toml TOML_STRING)
string(REPLACE ";" "\\\\;" TOML_STRING "${TOML_STRING}")
string(REPLACE "\n" ";" TOML_LINES "${TOML_STRING}")
foreach(TOML_LINE IN ITEMS ${TOML_LINES})
string(REGEX MATCH "^\\[(lib|package)\\]$" _ ${TOML_LINE})
if(CMAKE_MATCH_1 STREQUAL "lib")
set(CARGO_LIB_SECTION ON)
set(CARGO_PKG_SECTION OFF)
elseif(CMAKE_MATCH_1 STREQUAL "package")
set(CARGO_LIB_SECTION OFF)
set(CARGO_PKG_SECTION ON)
endif()
string(REGEX MATCH "^name += +\"([^\"]+)\"$" _ ${TOML_LINE})
if(CMAKE_MATCH_1 AND (CARGO_LIB_SECTION AND NOT CARGO_PKG_SECTION))
set(LIBRARY_NAME "${CMAKE_MATCH_1}")
elseif(CMAKE_MATCH_1 AND (NOT CARGO_LIB_SECTION AND CARGO_PKG_SECTION))
set(CARGO_PKG_NAME "${CMAKE_MATCH_1}")
endif()
string(REGEX MATCH "^version += +\"([^\"]+)\"$" _ ${TOML_LINE})
if(CMAKE_MATCH_1 AND CARGO_PKG_SECTION)
set(CARGO_PKG_VERSION "${CMAKE_MATCH_1}")
endif()
if(LIBRARY_NAME AND (CARGO_PKG_NAME AND CARGO_PKG_VERSION))
break()
endif()
endforeach()
project(${CARGO_PKG_NAME} VERSION ${CARGO_PKG_VERSION} LANGUAGES C DESCRIPTION "C bindings for the Automerge Rust backend.")
option(BUILD_SHARED_LIBS "Enable the choice of a shared or static library.")
option(BUILD_TESTING "Enable the choice of testing the build." ON)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
if(BUILD_TESTING)
include(CTest)
enable_testing()
endif()
string(MAKE_C_IDENTIFIER ${PROJECT_NAME} SYMBOL_PREFIX)
string(TOUPPER ${SYMBOL_PREFIX} SYMBOL_PREFIX)
add_subdirectory(src)
# Generate and install the configuration header.
math(EXPR INTEGER_PROJECT_VERSION_MAJOR "${PROJECT_VERSION_MAJOR} * 100000")
math(EXPR INTEGER_PROJECT_VERSION_MINOR "${PROJECT_VERSION_MINOR} * 100")
math(EXPR INTEGER_PROJECT_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
math(EXPR INTEGER_PROJECT_VERSION "${INTEGER_PROJECT_VERSION_MAJOR} + ${INTEGER_PROJECT_VERSION_MINOR} + ${INTEGER_PROJECT_VERSION_PATCH}")
configure_file(
${CMAKE_MODULE_PATH}/config.h.in
config.h
@ONLY
NEWLINE_STYLE LF
)
install(
FILES ${CMAKE_BINARY_DIR}/config.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
)
# Generate and install .cmake files
set(PROJECT_CONFIG_NAME "${PROJECT_NAME}-config")
set(PROJECT_CONFIG_VERSION_NAME "${PROJECT_CONFIG_NAME}-version")
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_CONFIG_VERSION_NAME}.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY ExactVersion
)
# The namespace label starts with the title-cased library name.
string(SUBSTRING ${LIBRARY_NAME} 0 1 NS_FIRST)
string(SUBSTRING ${LIBRARY_NAME} 1 -1 NS_REST)
string(TOUPPER ${NS_FIRST} NS_FIRST)
string(TOLOWER ${NS_REST} NS_REST)
string(CONCAT NAMESPACE ${NS_FIRST} ${NS_REST} "::")
# \note CMake doesn't automate the exporting of an imported library's targets so the package configuration script must do it.
configure_package_config_file(
${CMAKE_MODULE_PATH}/${PROJECT_CONFIG_NAME}.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_CONFIG_NAME}.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_CONFIG_NAME}.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_CONFIG_VERSION_NAME}.cmake
DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)

View file

@ -0,0 +1,98 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_dependency(Threads)
find_library(@SYMBOL_PREFIX@_IMPLIB_DEBUG @LIBRARY_NAME@${CMAKE_DEBUG_POSTFIX} PATHS "${PACKAGE_PREFIX_DIR}/debug/${CMAKE_INSTALL_LIBDIR}" "${PACKAGE_PREFIX_DIR}/${CMAKE_INSTALL_LIBDIR}" NO_DEFAULT_PATH)
find_library(@SYMBOL_PREFIX@_IMPLIB_RELEASE @LIBRARY_NAME@${CMAKE_RELEASE_POSTFIX} PATHS "${PACKAGE_PREFIX_DIR}/${CMAKE_INSTALL_LIBDIR}" NO_DEFAULT_PATH)
find_file(@SYMBOL_PREFIX@_LOCATION_DEBUG "${CMAKE_SHARED_LIBRARY_PREFIX}@LIBRARY_NAME@${CMAKE_DEBUG_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}" PATHS "${PACKAGE_PREFIX_DIR}/debug/${CMAKE_INSTALL_BINDIR}" "${PACKAGE_PREFIX_DIR}/${CMAKE_INSTALL_LIBDIR}" NO_DEFAULT_PATH)
find_file(@SYMBOL_PREFIX@_LOCATION_RELEASE "${CMAKE_SHARED_LIBRARY_PREFIX}@LIBRARY_NAME@${CMAKE_RELEASE_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}" PATHS "${PACKAGE_PREFIX_DIR}/${CMAKE_INSTALL_BINDIR}" NO_DEFAULT_PATH)
if(@BUILD_SHARED_LIBS@)
set(@SYMBOL_PREFIX@_DEFINE_SYMBOL "@SYMBOL_PREFIX@_EXPORTS")
if(WIN32)
set(@SYMBOL_PREFIX@_NO_SONAME_DEBUG "TRUE")
set(@SYMBOL_PREFIX@_NO_SONAME_RELEASE "TRUE")
set(@SYMBOL_PREFIX@_SONAME_DEBUG "")
set(@SYMBOL_PREFIX@_SONAME_RELEASE "")
else()
set(@SYMBOL_PREFIX@_NO_SONAME_DEBUG "FALSE")
set(@SYMBOL_PREFIX@_NO_SONAME_RELEASE "FALSE")
get_filename_component(@SYMBOL_PREFIX@_SONAME_DEBUG "${@SYMBOL_PREFIX@_LOCATION_DEBUG}" NAME)
get_filename_component(@SYMBOL_PREFIX@_SONAME_RELEASE "${@SYMBOL_PREFIX@_LOCATION_RELEASE}" NAME)
endif()
set(@SYMBOL_PREFIX@_TYPE "SHARED")
else()
set(@SYMBOL_PREFIX@_DEFINE_SYMBOL "")
set(@SYMBOL_PREFIX@_LOCATION_DEBUG "${@SYMBOL_PREFIX@_IMPLIB_DEBUG}")
set(@SYMBOL_PREFIX@_IMPLIB_DEBUG "")
set(@SYMBOL_PREFIX@_LOCATION_RELEASE "${@SYMBOL_PREFIX@_IMPLIB_RELEASE}")
set(@SYMBOL_PREFIX@_IMPLIB_RELEASE "")
set(@SYMBOL_PREFIX@_NO_SONAME_DEBUG "TRUE")
set(@SYMBOL_PREFIX@_NO_SONAME_RELEASE "TRUE")
set(@SYMBOL_PREFIX@_SONAME_DEBUG "")
set(@SYMBOL_PREFIX@_SONAME_RELEASE "")
set(@SYMBOL_PREFIX@_TYPE "STATIC")
endif()
add_library(@NAMESPACE@@PROJECT_NAME@ ${@SYMBOL_PREFIX@_TYPE} IMPORTED)
set_target_properties(
@NAMESPACE@@PROJECT_NAME@
PROPERTIES
# \note Cargo writes a debug build into a nested directory instead of decorating its name.
DEBUG_POSTFIX ""
DEFINE_SYMBOL "${@SYMBOL_PREFIX@_DEFINE_SYMBOL}"
IMPORTED_CONFIGURATIONS "RELEASE;DEBUG"
IMPORTED_IMPLIB_DEBUG "${@SYMBOL_PREFIX@_IMPLIB_DEBUG}"
IMPORTED_IMPLIB_RELEASE "${@SYMBOL_PREFIX@_IMPLIB_RELEASE}"
IMPORTED_LOCATION_DEBUG "${@SYMBOL_PREFIX@_LOCATION_DEBUG}"
IMPORTED_LOCATION_RELEASE "${@SYMBOL_PREFIX@_LOCATION_RELEASE}"
IMPORTED_NO_SONAME_DEBUG "${@SYMBOL_PREFIX@_NO_SONAME_DEBUG}"
IMPORTED_NO_SONAME_RELEASE "${@SYMBOL_PREFIX@_NO_SONAME_RELEASE}"
IMPORTED_SONAME_DEBUG "${@SYMBOL_PREFIX@_SONAME_DEBUG}"
IMPORTED_SONAME_RELEASE "${@SYMBOL_PREFIX@_SONAME_RELEASE}"
INTERFACE_INCLUDE_DIRECTORIES "${PACKAGE_PREFIX_DIR}/${CMAKE_INSTALL_INCLUDEDIR}"
LINKER_LANGUAGE C
PUBLIC_HEADER "${PACKAGE_PREFIX_DIR}/${CMAKE_INSTALL_INCLUDEDIR}/@PROJECT_NAME@/@LIBRARY_NAME@.h"
SOVERSION "@PROJECT_VERSION_MAJOR@"
VERSION "@PROJECT_VERSION@"
# \note Cargo exports all of the symbols automatically.
WINDOWS_EXPORT_ALL_SYMBOLS "TRUE"
)
# Remove the variables that the find_* command calls cached.
unset(@SYMBOL_PREFIX@_IMPLIB_DEBUG CACHE)
unset(@SYMBOL_PREFIX@_IMPLIB_RELEASE CACHE)
unset(@SYMBOL_PREFIX@_LOCATION_DEBUG CACHE)
unset(@SYMBOL_PREFIX@_LOCATION_RELEASE CACHE)
check_required_components(@PROJECT_NAME@)

View file

@ -0,0 +1,14 @@
#ifndef @SYMBOL_PREFIX@_CONFIG_INCLUDED
#define @SYMBOL_PREFIX@_CONFIG_INCLUDED
/* This header is auto-generated by CMake. */
#define @SYMBOL_PREFIX@_VERSION @INTEGER_PROJECT_VERSION@
#define @SYMBOL_PREFIX@_MAJOR_VERSION (@SYMBOL_PREFIX@_VERSION / 100000)
#define @SYMBOL_PREFIX@_MINOR_VERSION ((@SYMBOL_PREFIX@_VERSION / 100) % 1000)
#define @SYMBOL_PREFIX@_PATCH_VERSION (@SYMBOL_PREFIX@_VERSION % 100)
#endif /* @SYMBOL_PREFIX@_CONFIG_INCLUDED */

View file

@ -0,0 +1,196 @@
cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
find_program (
CARGO_CMD
"cargo"
PATHS "$ENV{CARGO_HOME}/bin"
DOC "The Cargo command"
)
if(NOT CARGO_CMD)
message(FATAL_ERROR "Cargo (Rust package manager) not found! Install it and/or set the CARGO_HOME environment variable.")
endif()
string(TOLOWER "${CMAKE_BUILD_TYPE}" BUILD_TYPE_LOWER)
if(BUILD_TYPE_LOWER STREQUAL debug)
set(CARGO_BUILD_TYPE "debug")
set(CARGO_FLAG "")
else()
set(CARGO_BUILD_TYPE "release")
set(CARGO_FLAG "--release")
endif()
set(CARGO_TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/Cargo/target")
set(CARGO_CURRENT_BINARY_DIR "${CARGO_TARGET_DIR}/${CARGO_BUILD_TYPE}")
set(
CARGO_OUTPUT
# \note cbindgen won't regenerate the header when it's missing so it can't be cleaned.
#${CARGO_TARGET_DIR}/${LIBRARY_NAME}.h
${CARGO_CURRENT_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}${LIBRARY_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}
${CARGO_CURRENT_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${LIBRARY_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX}
)
if(WIN32)
# \note The basename of an import library output by Cargo is the filename of its corresponding shared library.
list(APPEND CARGO_OUTPUT ${CARGO_CURRENT_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}${LIBRARY_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX})
endif()
add_custom_command(
OUTPUT ${CARGO_OUTPUT}
COMMAND
${CMAKE_COMMAND} -E env CARGO_TARGET_DIR=${CARGO_TARGET_DIR} ${CARGO_CMD} build ${CARGO_FLAG}
MAIN_DEPENDENCY
lib.rs
DEPENDS
${CMAKE_SOURCE_DIR}/build.rs
${CMAKE_SOURCE_DIR}/Cargo.toml
${CMAKE_SOURCE_DIR}/cbindgen.toml
WORKING_DIRECTORY
${CMAKE_SOURCE_DIR}
COMMENT
"Producing the library artifacts with Cargo..."
VERBATIM
)
if(BUILD_SHARED_LIBS)
if(WIN32)
set(LIBRARY_DESTINATION "${CMAKE_INSTALL_BINDIR}")
else()
set(LIBRARY_DESTINATION "${CMAKE_INSTALL_LIBDIR}")
endif()
set(LIBRARY_DEFINE_SYMBOL "${SYMBOL_PREFIX}_EXPORTS")
# \note The basename of an import library output by Cargo is the filename of its corresponding shared library.
set(LIBRARY_IMPLIB "${CARGO_CURRENT_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}${LIBRARY_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(LIBRARY_LOCATION "${CARGO_CURRENT_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}${LIBRARY_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}")
set(LIBRARY_NO_SONAME "${WIN32}")
set(LIBRARY_SONAME "${CMAKE_SHARED_LIBRARY_PREFIX}${LIBRARY_NAME}${CMAKE_${CMAKE_BUILD_TYPE}_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}")
set(LIBRARY_TYPE "SHARED")
else()
set(LIBRARY_DEFINE_SYMBOL "")
set(LIBRARY_DESTINATION "${CMAKE_INSTALL_LIBDIR}")
set(LIBRARY_IMPLIB "")
set(LIBRARY_LOCATION "${CARGO_CURRENT_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${LIBRARY_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(LIBRARY_NO_SONAME "TRUE")
set(LIBRARY_SONAME "")
set(LIBRARY_TYPE "STATIC")
endif()
add_library(${LIBRARY_NAME} ${LIBRARY_TYPE} IMPORTED GLOBAL)
target_sources(${LIBRARY_NAME} INTERFACE ${CARGO_OUTPUT})
set_target_properties(
${LIBRARY_NAME}
PROPERTIES
# \note Cargo writes a debug build into a nested directory instead of decorating its name.
DEBUG_POSTFIX ""
DEFINE_SYMBOL "${LIBRARY_DEFINE_SYMBOL}"
IMPORTED_IMPLIB "${LIBRARY_IMPLIB}"
IMPORTED_LOCATION "${LIBRARY_LOCATION}"
IMPORTED_NO_SONAME "${LIBRARY_NO_SONAME}"
IMPORTED_SONAME "${LIBRARY_SONAME}"
LINKER_LANGUAGE C
PUBLIC_HEADER "${CARGO_TARGET_DIR}/${LIBRARY_NAME}.h"
SOVERSION "${PROJECT_VERSION_MAJOR}"
VERSION "${PROJECT_VERSION}"
# \note Cargo exports all of the symbols automatically.
WINDOWS_EXPORT_ALL_SYMBOLS "TRUE"
)
target_compile_definitions(${LIBRARY_NAME} INTERFACE $<TARGET_PROPERTY:${LIBRARY_NAME},DEFINE_SYMBOL>)
target_include_directories(
${LIBRARY_NAME}
INTERFACE
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}>"
)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED)
set(LIBRARY_DEPENDENCIES Threads::Threads ${CMAKE_DL_LIBS})
if(WIN32)
list(APPEND LIBRARY_DEPENDENCIES Bcrypt userenv ws2_32)
else()
list(APPEND LIBRARY_DEPENDENCIES m)
endif()
target_link_libraries(${LIBRARY_NAME} INTERFACE ${LIBRARY_DEPENDENCIES})
install(
FILES $<TARGET_PROPERTY:${LIBRARY_NAME},IMPORTED_IMPLIB>
TYPE LIB
# \note The basename of an import library output by Cargo is the filename of its corresponding shared library.
RENAME "${CMAKE_STATIC_LIBRARY_PREFIX}${LIBRARY_NAME}${CMAKE_${CMAKE_BUILD_TYPE}_POSTFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}"
OPTIONAL
)
set(LIBRARY_FILE_NAME "${CMAKE_${LIBRARY_TYPE}_LIBRARY_PREFIX}${LIBRARY_NAME}${CMAKE_${CMAKE_BUILD_TYPE}_POSTFIX}${CMAKE_${LIBRARY_TYPE}_LIBRARY_SUFFIX}")
install(
FILES $<TARGET_PROPERTY:${LIBRARY_NAME},IMPORTED_LOCATION>
RENAME "${LIBRARY_FILE_NAME}"
DESTINATION ${LIBRARY_DESTINATION}
)
install(
FILES $<TARGET_PROPERTY:${LIBRARY_NAME},PUBLIC_HEADER>
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
)
if(BUILD_TESTING)
add_executable(test_${LIBRARY_NAME} ${CMAKE_SOURCE_DIR}/${LIBRARY_NAME}.c)
set_target_properties(test_${LIBRARY_NAME} PROPERTIES LINKER_LANGUAGE C)
# \note An imported library's INTERFACE_INCLUDE_DIRECTORIES property can't contain a non-existent path so its build-time
# include directory must be specified for all of its dependent targets instead.
target_include_directories(test_${LIBRARY_NAME} PRIVATE "$<BUILD_INTERFACE:${CARGO_TARGET_DIR}>")
target_link_libraries(test_${LIBRARY_NAME} PRIVATE ${LIBRARY_NAME})
add_test(NAME "test_${LIBRARY_NAME}" COMMAND test_${LIBRARY_NAME})
if(BUILD_SHARED_LIBS AND WIN32)
add_custom_command(
TARGET test_${LIBRARY_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CARGO_CURRENT_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}${LIBRARY_NAME}${CMAKE_${CMAKE_BUILD_TYPE}_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}
${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Copying the DLL built by Cargo into the test directory..."
VERBATIM
)
endif()
add_custom_command(
TARGET test_${LIBRARY_NAME}
POST_BUILD
COMMAND
ctest -C $<CONFIGURATION> --output-on-failure
COMMENT
"Running the test(s)..."
VERBATIM
)
endif()