Skip to content

Commit

Permalink
Add cmake and an example (#23)
Browse files Browse the repository at this point in the history
Adds CMake to both power Cargo compilation and generation of header files with cbindgen, and enable easy use of the FFI from other languages with a more accessible build system.
  • Loading branch information
alilleybrinker authored Jun 22, 2020
1 parent 9d7986a commit 8e234e9
Show file tree
Hide file tree
Showing 12 changed files with 599 additions and 1 deletion.
1 change: 1 addition & 0 deletions rust/pact_matching_ffi/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build*
299 changes: 299 additions & 0 deletions rust/pact_matching_ffi/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
#################################################################################################
# CMAKE VERSION
#################################################################################################

# Set the minimum to 3.15. This is arbitrary and we should probably try to
# test everything with older CMake versions once this is all written, to
# figure out an actual lower-bound.
cmake_minimum_required(VERSION 3.15...3.17)

# Set policies appropriately, so it knows when to warn about policy
# violations.
if(${CMAKE_VERSION} VERSION_LESS 3.17)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.17)
endif()

#################################################################################################
# CONFIG FILES
#
# Set the location of various config files we'll use throughout.
#################################################################################################

# The path to the cargo config file.
set(CARGO_CONFIG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml")

# The path to the cbindgen config file.
set(CBINDGEN_CONFIG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cbindgen.toml")

#################################################################################################
# PROJECT NAME
#
# This is pulled from the `Cargo.toml` file.
#################################################################################################

# Regex for 'name = "<name>"'
set(NAME_REGEX "name = \"(.+)\"")

# Read in the line containing the name
file(STRINGS ${CARGO_CONFIG_FILE} NAME_STRING REGEX ${NAME_REGEX})

# Pick out just the name
string(REGEX REPLACE ${NAME_REGEX} "\\1" NAME_STRING "${NAME_STRING}")

#################################################################################################
# PROJECT VERSION
#
# This is pulled from the `Cargo.toml` file.
#################################################################################################

# Regex for 'version = "<version>"'
set(VERSION_REGEX "^version = \"(.+)\"$")

# Read in the line containing the version
file(STRINGS ${CARGO_CONFIG_FILE} VERSION_STRING REGEX ${VERSION_REGEX})

# Pick out just the version
string(REGEX REPLACE ${VERSION_REGEX} "\\1" VERSION_STRING "${VERSION_STRING}")

#################################################################################################
# PROJECT DESCRIPTION
#
# This is pulled from the `Cargo.toml` file.
#################################################################################################

# Regex for 'description = "<description>"'
set(DESCRIPTION_REGEX "description = \"(.+)\"")

# Read in the line containing the description
file(STRINGS ${CARGO_CONFIG_FILE} DESCRIPTION_STRING REGEX ${DESCRIPTION_REGEX})

# Pick out just the description
string(REGEX REPLACE ${DESCRIPTION_REGEX} "\\1" DESCRIPTION_STRING "${DESCRIPTION_STRING}")

#################################################################################################
# PROJECT DECLARATION
#################################################################################################

# Print message indicating we found the crate information.
message("Found crate ${NAME_STRING} (version ${VERSION_STRING}): ${DESCRIPTION_STRING}")

# Define the project for the current file.
project(
${NAME_STRING}
VERSION ${VERSION_STRING}
DESCRIPTION ${DESCRIPTION_STRING}
LANGUAGES NONE)

#################################################################################################
# CMAKE UTILITIES
#
# Add CMake utilities for finding Cargo and Cbindgen to the module path.
#################################################################################################

set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})

#################################################################################################
# OUT OF SOURCE BUILDS
#
# Require out-of-source builds for this project. It keeps things much simpler
# and cleaner.
#################################################################################################

# Set a path to the CMake config (this file)
file(TO_CMAKE_PATH "${PROJECT_BINARY_DIR}/CMakeLists.txt" LOC_PATH)

# Define the error message to potentially be printed.
set(OOS_MSG "\
You cannot build in a source directory (or any directory with a CMakeLists.txt file). \
Please make a build subdirectory. \
Feel free to remove CMakeCache.txt and CMakeFiles.
")

# If that file path exists, we're doing an in-source build, so we should exit with a fatal
# error complaining only out-of-source builds are supported.
if(EXISTS ${LOC_PATH})
message(FATAL_ERROR ${OOS_MSG})
endif()

#################################################################################################
# DEFAULT BUILD TYPE
#
# Make release the default build type
#################################################################################################

# The default build type is Release.
set(default_build_type "Release")

if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
# Tell the user they're getting the default build type.
message(STATUS "Setting build type to '${default_build_type}' as none was specified.")

# Cache the build type.
set(CMAKE_BUILD_TYPE ${default_build_type} CACHE STRING "Choose the type of build." FORCE)

# Set the possible values of build type
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release")
endif()

#################################################################################################
# Find Cargo & Cbindgen
#
# Uses custom finders to locate the cargo and cbindgen executables and identify their versions
#################################################################################################

# Uses the finder specified in `cmake/FindCargo.cmake`
find_package(Cargo REQUIRED)

# Uses the finder specified in `cmake/FindCbindgen.cmake`
find_package(Cbindgen REQUIRED)

#################################################################################################
# VARIABLES
#
# Sets important variables to be used by the custom targets.
#################################################################################################

# Set cargo build type flag, and name of the folder containing the library file,
# based on the configured build type.
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CARGO_BUILD_TYPE "")
set(TARGET_TYPE "debug")
else()
set(CARGO_BUILD_TYPE "--release")
set(TARGET_TYPE "release")
endif()

# There could be something more generic here using `cargo metadata` output to discover
# the workspace root and get the target dir from that, and in a more general context
# that's what ought to be done, but for now we know where the target dir is, and should
# just reuse it.
set(CARGO_TARGET_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../target")

# The name of the crate itself.
set(CRATE_NAME ${NAME_STRING})

# Set the CARGO_LIBRARY_NAME based on whether we're on Windows
if(WIN32)
set(CARGO_LIBRARY_NAME "${CRATE_NAME}.dll")
else()
set(CARGO_LIBRARY_NAME "${CRATE_NAME}.a")
endif()

# The path to the library file.
set(CARGO_LIBRARY_FILE "${CARGO_TARGET_DIR}/${TARGET_TYPE}/${CARGO_LIBRARY_NAME}")

# On Windows, dynamic linking requires both a .dll file (which will be dynamically
# linked), and a library file (which describes _how_ to link to the DLL, and will
# itself be statically linked). We need to make sure both are included on installation.
#
# For Rust specifically, the name of the .lib file is the name of the .dll file with
# .lib appended (so the extension is .dll.lib).
if(WIN32)
set(CARGO_INSTALL_FILES ${CARGO_LIBRARY_FILE} "${CARGO_LIBRARY_FILE}.lib")
else()
set(CARGO_INSTALL_FILES ${CARGO_LIBRARY_FILE})
endif()

# Name of the header file.
set(CBINDGEN_HEADER_NAME "pact_matching.h")

# Path to the header file.
set(CBINDGEN_HEADER_FILE "${CMAKE_CURRENT_SOURCE_DIR}/include/${CBINDGEN_HEADER_NAME}")

#################################################################################################
# LIBRARY
#
# Defines the target for building the library file.
#################################################################################################

# Defines the cargo command to build the library file.
add_custom_command(
OUTPUT ${CARGO_LIBRARY_FILE}
COMMAND
${CARGO_EXECUTABLE}
build
${CARGO_BUILD_TYPE}
--target-dir ${CARGO_TARGET_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

# Add target for the library file
add_custom_target(build_library ALL
COMMENT "Building library file with cargo"
DEPENDS ${CARGO_LIBRARY_FILE})

# Teach CMake to install the library file(s) built by the pact_matching_ffi target
install(FILES ${CARGO_INSTALL_FILES} TYPE LIB)

#################################################################################################
# HEADER
#
# Defines the target for generating the header file.
#################################################################################################

# Defines the cbindgen command to generate the header file.
add_custom_command(
OUTPUT ${CBINDGEN_HEADER_FILE}
COMMAND
${CBINDGEN_EXECUTABLE}
--config ${CBINDGEN_CONFIG_FILE}
--crate ${CRATE_NAME}
--output ${CBINDGEN_HEADER_FILE}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

# Add target for the header file
add_custom_target(generate_header ALL
COMMENT "Generating header file with cbindgen"
DEPENDS ${CBINDGEN_HEADER_FILE})

# Teach CMake to install the header file built by the generate_header target
install(FILES "${CBINDGEN_HEADER_FILE}" TYPE INCLUDE)

#################################################################################################
# PACKAGE FILES
#
# Defines packaging information for the current project, so it can be used via `find_package`.
#################################################################################################

# Bring in some functions to help generate the config files.
include(CMakePackageConfigHelpers)

# Set the name of the final package configuration file.
set(PACKAGE_CONFIG_NAME "PactMatchingFfiConfig.cmake")

# Set the name of the final package version file.
set(PACKAGE_VERSION_NAME "PactMatchingFfiConfigVersion.cmake")

# Set the path to the package configuration input file.
set(PACKAGE_CONFIG_INPUT_FILE "cmake/${PACKAGE_CONFIG_NAME}.in")

# Set the path to the package configuration output file.
set(PACKAGE_CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_CONFIG_NAME}")

# Set locations in the installation prefix to put the relevant files.
set(INCLUDE_INSTALL_DIR "include")
set(LIB_INSTALL_DIR "lib")

# Relative path (within install prefix) to place the configuration file.
set(PACKAGE_CONFIG_INSTALL_DIR "${LIB_INSTALL_DIR}/cmake")

# Set the path to the generated package version file.
set(PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_VERSION_NAME}")

# Generate the configuration file from the input and put it in the right place
configure_package_config_file(
# Input file
${PACKAGE_CONFIG_INPUT_FILE}
# Output file
${PACKAGE_CONFIG_FILE}
# Installation destination within the install prefix dir
INSTALL_DESTINATION ${PACKAGE_CONFIG_INSTALL_DIR}
# Variables to pass along to the input file
PATH_VARS INCLUDE_INSTALL_DIR LIB_INSTALL_DIR)

# Generate the version file based on the project version info.
write_basic_package_version_file(${PACKAGE_VERSION_FILE} COMPATIBILITY SameMajorVersion)

# Install config files in the proper directory.
install(FILES ${PACKAGE_CONFIG_FILE} ${PACKAGE_VERSION_FILE} DESTINATION ${PACKAGE_CONFIG_INSTALL_DIR})
2 changes: 1 addition & 1 deletion rust/pact_matching_ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ log = "0.4.8"
serde_json = "1.0.51"

[lib]
crate-type = ["cdylib", "staticlib", "rlib"]
crate-type = ["cdylib"]
Loading

0 comments on commit 8e234e9

Please sign in to comment.