From 8e234e933e0e469f5123b8a6881342654bb4f156 Mon Sep 17 00:00:00 2001
From: Andrew Lilley Brinker <andrewbrinker@me.com>
Date: Mon, 22 Jun 2020 06:29:28 -0700
Subject: [PATCH] Add cmake and an example (#23)

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.
---
 rust/pact_matching_ffi/.gitignore             |   1 +
 rust/pact_matching_ffi/CMakeLists.txt         | 299 ++++++++++++++++++
 rust/pact_matching_ffi/Cargo.toml             |   2 +-
 rust/pact_matching_ffi/cbindgen.toml          | 103 ++++++
 rust/pact_matching_ffi/cmake/FindCargo.cmake  |  20 ++
 .../cmake/FindCbindgen.cmake                  |  20 ++
 .../cmake/PactMatchingFFIConfig.cmake.in      |  27 ++
 rust/pact_matching_ffi/example/.gitignore     |   1 +
 rust/pact_matching_ffi/example/CMakeLists.txt |  85 +++++
 rust/pact_matching_ffi/example/src/main.c     |  41 +++
 rust/pact_matching_ffi/include/.gitignore     |   1 +
 rust/pact_matching_ffi/include/.gitkeep       |   0
 12 files changed, 599 insertions(+), 1 deletion(-)
 create mode 100644 rust/pact_matching_ffi/.gitignore
 create mode 100644 rust/pact_matching_ffi/CMakeLists.txt
 create mode 100644 rust/pact_matching_ffi/cbindgen.toml
 create mode 100644 rust/pact_matching_ffi/cmake/FindCargo.cmake
 create mode 100644 rust/pact_matching_ffi/cmake/FindCbindgen.cmake
 create mode 100644 rust/pact_matching_ffi/cmake/PactMatchingFFIConfig.cmake.in
 create mode 100644 rust/pact_matching_ffi/example/.gitignore
 create mode 100644 rust/pact_matching_ffi/example/CMakeLists.txt
 create mode 100644 rust/pact_matching_ffi/example/src/main.c
 create mode 100644 rust/pact_matching_ffi/include/.gitignore
 create mode 100644 rust/pact_matching_ffi/include/.gitkeep

diff --git a/rust/pact_matching_ffi/.gitignore b/rust/pact_matching_ffi/.gitignore
new file mode 100644
index 000000000..0ec9e7f98
--- /dev/null
+++ b/rust/pact_matching_ffi/.gitignore
@@ -0,0 +1 @@
+/build*
diff --git a/rust/pact_matching_ffi/CMakeLists.txt b/rust/pact_matching_ffi/CMakeLists.txt
new file mode 100644
index 000000000..e565b460f
--- /dev/null
+++ b/rust/pact_matching_ffi/CMakeLists.txt
@@ -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})
diff --git a/rust/pact_matching_ffi/Cargo.toml b/rust/pact_matching_ffi/Cargo.toml
index 9bcbf89be..f4fa8ef44 100644
--- a/rust/pact_matching_ffi/Cargo.toml
+++ b/rust/pact_matching_ffi/Cargo.toml
@@ -22,4 +22,4 @@ log = "0.4.8"
 serde_json = "1.0.51"
 
 [lib]
-crate-type = ["cdylib", "staticlib", "rlib"]
+crate-type = ["cdylib"]
diff --git a/rust/pact_matching_ffi/cbindgen.toml b/rust/pact_matching_ffi/cbindgen.toml
new file mode 100644
index 000000000..595f4fcb5
--- /dev/null
+++ b/rust/pact_matching_ffi/cbindgen.toml
@@ -0,0 +1,103 @@
+# This is a template cbindgen.toml file with all of the default values.
+# Some values are commented out because their absence is the real default.
+#
+# See https://github.com/eqrion/cbindgen/blob/master/docs.md#cbindgentoml
+# for detailed documentation of every option here.
+
+language = "C"
+
+############## Options for Wrapping the Contents of the Header #################
+
+# header = "/* Text to put at the beginning of the generated file. Probably a license. */"
+# trailer = "/* Text to put at the end of the generated file */"
+include_guard = "pact_matching_ffi_h"
+autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
+include_version = true
+namespace = "pact_matching"
+namespaces = []
+using_namespaces = []
+sys_includes = []
+includes = []
+no_includes = false
+cpp_compat = true
+
+############################ Code Style Options ################################
+
+braces = "SameLine"
+line_length = 100
+tab_width = 2
+documentation_style = "auto"
+
+############################# Codegen Options ##################################
+
+style = "both"
+
+[defines]
+# "target_os = freebsd" = "DEFINE_FREEBSD"
+# "feature = serde" = "DEFINE_SERDE"
+
+[export]
+include = []
+exclude = []
+# prefix = "CAPI_"
+item_types = []
+renaming_overrides_prefixing = false
+
+[export.rename]
+
+[export.body]
+
+[fn]
+rename_args = "None"
+# must_use = "MUST_USE_FUNC"
+# prefix = "START_FUNC"
+# postfix = "END_FUNC"
+args = "auto"
+sort_by = "None"
+
+[struct]
+rename_fields = "None"
+# must_use = "MUST_USE_STRUCT"
+derive_constructor = false
+derive_eq = false
+derive_neq = false
+derive_lt = false
+derive_lte = false
+derive_gt = false
+derive_gte = false
+
+[enum]
+rename_variants = "None"
+# must_use = "MUST_USE_ENUM"
+add_sentinel = false
+prefix_with_name = true
+derive_helper_methods = false
+derive_const_casts = false
+derive_mut_casts = false
+# cast_assert_name = "ASSERT"
+derive_tagged_enum_destructor = false
+derive_tagged_enum_copy_constructor = false
+enum_class = true
+private_default_tagged_enum_constructor = false
+
+[const]
+allow_static_const = true
+allow_constexpr = false
+
+[macro_expansion]
+bitflags = false
+
+############## Options for How Your Rust library Should Be Parsed ##############
+
+[parse]
+parse_deps = true
+include = ["pact_matching"]
+exclude = []
+clean = false
+extra_bindings = []
+
+[parse.expand]
+crates = []
+all_features = false
+default_features = true
+features = []
diff --git a/rust/pact_matching_ffi/cmake/FindCargo.cmake b/rust/pact_matching_ffi/cmake/FindCargo.cmake
new file mode 100644
index 000000000..1f0c2e482
--- /dev/null
+++ b/rust/pact_matching_ffi/cmake/FindCargo.cmake
@@ -0,0 +1,20 @@
+
+# Find Cargo, possibly in ~/.cargo. Make sure to check in any `bin` subdirectories
+# on the program search path
+find_program(CARGO_EXECUTABLE cargo PATHS "$ENV{HOME}/.cargo" PATH_SUFFIXES bin)
+
+# If we found it, see if we can get its version.
+if(CARGO_EXECUTABLE)
+    execute_process(COMMAND ${CARGO_EXECUTABLE} -V OUTPUT_VARIABLE CARGO_VERSION_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE)
+    
+    if(CARGO_VERSION_OUTPUT MATCHES "cargo ([0-9]+\\.[0-9]+\\.[0-9]+).*")
+        set(CARGO_VERSION ${CMAKE_MATCH_1})
+    endif()
+endif()
+
+# Hides the CARGO_EXECUTABLE variable unless advanced variables are requested
+mark_as_advanced(CARGO_EXECUTABLE)
+
+# Require that we find both the executable and the version. Otherwise error out.
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Cargo REQUIRED_VARS CARGO_EXECUTABLE CARGO_VERSION VERSION_VAR CARGO_VERSION)
diff --git a/rust/pact_matching_ffi/cmake/FindCbindgen.cmake b/rust/pact_matching_ffi/cmake/FindCbindgen.cmake
new file mode 100644
index 000000000..b0431be3b
--- /dev/null
+++ b/rust/pact_matching_ffi/cmake/FindCbindgen.cmake
@@ -0,0 +1,20 @@
+
+# Find Cbindgen, possibly in ~/.cargo. Make sure to check in any `bin` subdirectories
+# on the program search path
+find_program(CBINDGEN_EXECUTABLE cbindgen PATHS "$ENV{HOME}/.cargo" PATH_SUFFIXES bin)
+
+# If we found it, see if we can get its version.
+if(CBINDGEN_EXECUTABLE)
+    execute_process(COMMAND ${CBINDGEN_EXECUTABLE} -V OUTPUT_VARIABLE CBINDGEN_VERSION_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE)
+    
+    if(CBINDGEN_VERSION_OUTPUT MATCHES "cbindgen ([0-9]+\\.[0-9]+\\.[0-9]+).*")
+        set(CBINDGEN_VERSION ${CMAKE_MATCH_1})
+    endif()
+endif()
+
+# Hides the CBINDGEN_EXECUTABLE variable unless advanced variables are requested
+mark_as_advanced(CBINDGEN_EXECUTABLE)
+
+# Require that we find both the executable and the version. Otherwise error out.
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Cbindgen REQUIRED_VARS CBINDGEN_EXECUTABLE CBINDGEN_VERSION VERSION_VAR CBINDGEN_VERSION)
diff --git a/rust/pact_matching_ffi/cmake/PactMatchingFFIConfig.cmake.in b/rust/pact_matching_ffi/cmake/PactMatchingFFIConfig.cmake.in
new file mode 100644
index 000000000..df9860985
--- /dev/null
+++ b/rust/pact_matching_ffi/cmake/PactMatchingFFIConfig.cmake.in
@@ -0,0 +1,27 @@
+
+@PACKAGE_INIT@
+
+set_and_check(PactMatchingFfi_INCLUDE_DIRS "@PACKAGE_INCLUDE_INSTALL_DIR@")
+
+if(WIN32)
+    set(LIB_NAME "@PACKAGE_LIB_INSTALL_DIR@/pact_matching_ffi.dll")
+    set(IMPORT_NAME "${LIB_NAME}.lib")
+else()
+    set(LIB_NAME "@PACKAGE_LIB_INSTALL_DIR@/pact_matching_ffi.a")
+    set(IMPORT_NAME "")
+endif()
+
+set(HEADER_NAME "@PACKAGE_INCLUDE_INSTALL_DIR@/pact_matching.h")
+
+set_and_check(PactMatchingFfi_LIBRARIES ${LIB_NAME} ${IMPORT_NAME})
+
+add_library(PactMatchingFfi SHARED IMPORTED)
+
+set_target_properties(PactMatchingFfi PROPERTIES
+    IMPORTED_LOCATION ${LIB_NAME}
+    IMPORTED_IMPLIB ${IMPORT_NAME}
+    INTERFACE_INCLUDE_DIRECTORIES @PACKAGE_INCLUDE_INSTALL_DIR@
+    INSTALL_RPATH @PACKAGE_LIB_INSTALL_DIR@
+    BUILD_RPATH @PACKAGE_LIB_INSTALL_DIR@)
+
+check_required_components(PactMatchingFfi)
diff --git a/rust/pact_matching_ffi/example/.gitignore b/rust/pact_matching_ffi/example/.gitignore
new file mode 100644
index 000000000..567609b12
--- /dev/null
+++ b/rust/pact_matching_ffi/example/.gitignore
@@ -0,0 +1 @@
+build/
diff --git a/rust/pact_matching_ffi/example/CMakeLists.txt b/rust/pact_matching_ffi/example/CMakeLists.txt
new file mode 100644
index 000000000..eee02b890
--- /dev/null
+++ b/rust/pact_matching_ffi/example/CMakeLists.txt
@@ -0,0 +1,85 @@
+#################################################################################################
+# 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()
+
+#################################################################################################
+# PROJECT DECLARATION
+#################################################################################################
+
+project(C_EXAMPLE_1
+        VERSION "0.1.0"
+        DESCRIPTION "A basic example of a C consumer for the pact matching FFI"
+        LANGUAGES C)
+
+#################################################################################################
+# 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
+#################################################################################################
+
+set(default_build_type "Release")
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+  message(STATUS "Setting build type to '${default_build_type}' as none was specified.")
+  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 PACT MATCHING FFI
+#
+# This ensures CMake can find the pact matching FFI library file
+#################################################################################################
+
+# Sets the search path to the location of the package config
+get_filename_component(REAL_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE)
+set(SEARCH_PATH "${REAL_ROOT}/build/install/lib/cmake")
+
+# Find the pact matching FFI package and load the imported target
+find_package(PactMatchingFfi REQUIRED CONFIG PATHS ${SEARCH_PATH})
+
+#################################################################################################
+# BUILD
+#################################################################################################
+
+# Define the executable
+add_executable(example src/main.c)
+
+# Link to pact matching FFI
+target_link_libraries(example PRIVATE PactMatchingFfi)
diff --git a/rust/pact_matching_ffi/example/src/main.c b/rust/pact_matching_ffi/example/src/main.c
new file mode 100644
index 000000000..b322022d3
--- /dev/null
+++ b/rust/pact_matching_ffi/example/src/main.c
@@ -0,0 +1,41 @@
+
+#include "pact_matching.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+#define ERROR_MSG_LEN 256
+
+int main(void) {
+    logger_init();
+    logger_attach_sink("stdout", LevelFilter_Trace);
+    logger_apply();
+
+    Message *msg = message_new();
+    int error = message_delete(msg);
+
+    if (error == EXIT_FAILURE) {
+        char error_msg[ERROR_MSG_LEN];
+
+        int error = get_error_message(error_msg, ERROR_MSG_LEN);
+
+        printf("%s\n", error_msg);
+
+        return EXIT_FAILURE;
+    }
+
+    char *json_str = "{\
+        \"description\": \"String\",\
+        \"providerState\": \"provider state\",\
+        \"matchingRules\": {}\
+    }";
+    Message *msg_json = message_from_json(0, json_str, PactSpecification_V3);
+
+    if (NULL == msg_json) {
+        char error_msg[ERROR_MSG_LEN];
+        int error = get_error_message(error_msg, ERROR_MSG_LEN);
+        printf("%s\n", error_msg);
+        return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+}
diff --git a/rust/pact_matching_ffi/include/.gitignore b/rust/pact_matching_ffi/include/.gitignore
new file mode 100644
index 000000000..aec901578
--- /dev/null
+++ b/rust/pact_matching_ffi/include/.gitignore
@@ -0,0 +1 @@
+pact_matching.h
diff --git a/rust/pact_matching_ffi/include/.gitkeep b/rust/pact_matching_ffi/include/.gitkeep
new file mode 100644
index 000000000..e69de29bb