diff --git a/.travis.yml b/.travis.yml index e8657f7..b864a10 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -sudo: required +sudo: false language: c # using anchor to import sources into linux builds @@ -10,6 +10,11 @@ addons: - llvm-toolchain-precise-3.7 - llvm-toolchain-precise +# important for allowed-to-fail matching +# see https://docs.travis-ci.com/user/customizing-the-build#Rows-that-are-Allowed-to-Fail +env: + - ALLOWED_TO_FAIL=0 + # travis currently does not support directly setting gcc/clang with versions # (e.g. gcc-4.8) as value for the compiler key. So we will have to manually # request these packages and use environment varibles to create the matrix. @@ -19,39 +24,55 @@ addons: matrix: include: # gcc 4.8 on linux - - env: C_COMPILER=gcc-4.8 + - env: + - C_COMPILER=gcc-4.8 addons: apt: <<: *apt - packages: gcc-4.8 + packages: + - gcc-4.8 + - libsubunit-dev + # gcc 4.9 on linux - - env: C_COMPILER=gcc-4.9 + - env: + - C_COMPILER=gcc-4.9 addons: apt: <<: *apt - packages: gcc-4.9 + packages: + - gcc-4.9 + - libsubunit-dev # gcc 5 on linux - - env: C_COMPILER=gcc-5 + - env: + - C_COMPILER=gcc-5 addons: apt: <<: *apt - packages: gcc-5 + packages: + - gcc-5 + - libsubunit-dev # clang 3.6 on linux - - env: C_COMPILER=clang-3.6 + - env: + - C_COMPILER=clang-3.6 addons: apt: <<: *apt - packages: clang-3.6 + packages: + - clang-3.6 + - libsubunit-dev # clang 3.7 on linux - - env: C_COMPILER=clang-3.7 + - env: + - C_COMPILER=clang-3.7 addons: apt: <<: *apt - packages: clang-3.7 + packages: + - clang-3.7 + - libsubunit-dev ## gcc 4.8 on osx #- os: osx @@ -65,37 +86,39 @@ matrix: #- os: osx # env: FORMULA=gcc5 COMPILER=gcc C_COMPILER=gcc-5 - # clang 3.6 on osx + # OSX 10.13 + # Apple LLVM version 9.1.0 (clang-902.0.39.2) + # Target: x86_64-apple-darwin17.6.0 - os: osx - osx_image: xcode6.4 - env: C_COMPILER=clang + osx_image: xcode9.4 + env: + - C_COMPILER=clang + - ALLOWED_TO_FAIL=1 - # clang 3.7 on osx + # OSX 10.12 + # Apple LLVM version 9.0.0 (clang-900.0.39.2) + # Target: x86_64-apple-darwin16.7.0 - os: osx - osx_image: xcode7.1 - env: C_COMPILER=clang + osx_image: xcode9.2 + env: + - C_COMPILER=clang + - ALLOWED_TO_FAIL=1 - # # clang 4.2 on osx - # - os: osx - # osx_image: xcode8.2 - # env: C_COMPILER=clang + allow_failures: + - os: osx + osx_image: xcode9.4 + env: + - C_COMPILER=clang + - ALLOWED_TO_FAIL=1 + - os: osx + osx_image: xcode9.2 + env: + - C_COMPILER=clang + - ALLOWED_TO_FAIL=1 before_install: - # for osx: 0. update brew; 1. install cmake if missing; 2. (gcc) unlink pre-installed gcc; 3. (gcc) install desired version of gcc - - 'if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi' - - 'if [[ "$TRAVIS_OS_NAME" == "osx" && -z "$(which cmake)" ]]; then brew install cmake; fi' # xcode 7.1 is missing cmake - - 'if [[ "$TRAVIS_OS_NAME" == "osx" && "$COMPILER" == "gcc" ]]; then brew unlink gcc || true; fi' # ignore unlink errors - - 'if [[ "$TRAVIS_OS_NAME" == "osx" && "$COMPILER" == "gcc" ]]; then brew unlink $FORMULA || true; fi' # ignore unlink errors - - 'if [[ "$TRAVIS_OS_NAME" == "osx" && "$COMPILER" == "gcc" ]]; then brew install $FORMULA; fi' - - export CC=$C_COMPILER - - wget https://github.com/libcheck/check/releases/download/0.11.0/check-0.11.0.tar.gz - - tar xvfz check-0.11.0.tar.gz - - cd check-0.11.0 && ./configure && make && sudo make install && cd .. + - ./ci/before-install.sh script: - - mkdir _build && cd _build - - cmake .. - - make -j - - make check - - egrep -r ":F:|:E:" . || true + - ./ci/run.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 1dfce90..e461353 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,28 @@ option(HAVE_STATS "stats enabled by default" ON) option(HAVE_DEBUG_MM "debugging oriented memory management disabled by default" OFF) option(COVERAGE "code coverage" OFF) +if(BUILD_AND_INSTALL_CHECK) + # (simms) What follows is a crime against build systems as we run the build/install + # for the check library up front, during the planning phase. + + set(LIBCHECK_PREFIX "${CMAKE_BINARY_DIR}/check") + + # check for a local install of check + if(NOT EXISTS "${LIBCHECK_PREFIX}") + # (simms) This is terrible and I did it this way to ensure this gets built + # before the rest of the 'check' tests run. This should be rewritten so that + # the other dependencies know that there's a target that can build check + execute_process( + COMMAND "bash" "${PROJECT_SOURCE_DIR}/ci/install-check.sh" "${LIBCHECK_PREFIX}" + TIMEOUT 300 # if this doesn't build in 5 minutes something is hosed + ) + endif() + + set(CHECK_ROOT_DIR "${LIBCHECK_PREFIX}") + set(CMAKE_REQUIRED_INCLUDES "${CHECK_ROOT_DIR}/include") # these make check link correctly in ccommon and pelikan + set(CMAKE_REQUIRED_LIBRARIES "${CHECK_ROOT_DIR}/lib") +endif() + include(CheckIncludeFiles) if(OS_PLATFORM STREQUAL "OS_LINUX") check_include_files(linux/time64.h HAVE_TIME64) @@ -107,6 +129,7 @@ endif(COVERAGE) # test dependencies include(FindPackageHandleStandardArgs) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") + find_package(Check) if(NOT CHECK_FOUND) message(WARNING "Check is required to build and run tests") @@ -120,6 +143,10 @@ endif(CHECK_FOUND) find_package(Threads) +enable_language(Rust) +include(CMakeCargo) + + # where to find include files include_directories(${include_directories} "${PROJECT_BINARY_DIR}" diff --git a/ci/before-install.sh b/ci/before-install.sh new file mode 100755 index 0000000..d948672 --- /dev/null +++ b/ci/before-install.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -euo pipefail +IFS=$'\n\t' + +die() { echo "fatal: $*" >&2; exit 1; } + +TEMP="$(mktemp -d -t TEMP.XXXXXXX)" || die "failed to make tmpdir" +cleanup() { [[ -n "${TEMP:-}" ]] && rm -rf "${TEMP}"; } +trap cleanup EXIT + +TOPLEVEL="$(git -C "$(cd "$(dirname "$0")" >/dev/null || exit 1; pwd)" rev-parse --show-toplevel)" || die 'failed to find TOPLEVEL' + +# for osx: 0. update brew; 1. install cmake if missing; 2. (gcc) unlink pre-installed gcc; 3. (gcc) install desired version of gcc + +if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + brew update &>/dev/null + brew install cmake || true # xcode 8.1 is missing cmake + + if [[ "$C_COMPILER" =~ ^gcc && -n "${FORMULA:-}" ]]; then + brew unlink gcc || true + brew unlink "$FORMULA" || true + brew install "$FORMULA" + fi +fi + +export CC="$C_COMPILER" + +curl https://sh.rustup.rs -sSf | sh -s -- -y diff --git a/ci/install-check.sh b/ci/install-check.sh new file mode 100755 index 0000000..628d6d2 --- /dev/null +++ b/ci/install-check.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +set -euo pipefail +IFS=$'\n\t' + +die() { echo "fatal: $*" >&2; exit 1; } + +if [[ $# -lt 1 ]]; then + echo "Usage: $0 check-install-path" + exit 1 +fi + +CHECK_PREFIX="$1" +shift + +TEMP="$(mktemp -d -t TEMP.XXXXXXX)" || die "failed to make tmpdir" +cleanup() { [[ -n "${TEMP:-}" ]] && rm -rf "${TEMP}"; } +trap cleanup EXIT + +TOPLEVEL="$(git -C "$(cd "$(dirname "$0")" >/dev/null || exit 1; pwd)" rev-parse --show-toplevel)" || die 'failed to find TOPLEVEL' + +CHECK_VERSION=0.12.0 +CHECK_TARBALL="check-${CHECK_VERSION}.tar.gz" +CHECK_DIR="check-${CHECK_VERSION}" + +( + cd "$TEMP" && + wget "https://github.com/libcheck/check/releases/download/${CHECK_VERSION}/${CHECK_TARBALL}" && + tar xvfz "${CHECK_TARBALL}" && + cd "${CHECK_DIR}" && + ./configure --prefix="$CHECK_PREFIX" && + make && + make install +) || die "check build failed" diff --git a/ci/run.sh b/ci/run.sh new file mode 100755 index 0000000..9e4e9a2 --- /dev/null +++ b/ci/run.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -uo pipefail +IFS=$'\n\t' + +die() { echo "fatal: $*" >&2; exit 1; } + +export PATH=$HOME/.cargo/bin:$PATH + +mkdir -p _build && ( cd _build && cmake -D BUILD_AND_INSTALL_CHECK=yes .. && make -j && make check ) +RESULT=$? + +egrep -r ":F:|:E:" . |grep -v 'Binary file' || true + + +if [[ $RESULT -ne 0 ]]; then + echo "Build failure" >&2 + exit $RESULT +else + echo "success!" >&2 + exit 0 +fi diff --git a/cmake/CMakeCargo.cmake b/cmake/CMakeCargo.cmake new file mode 100644 index 0000000..975b0cc --- /dev/null +++ b/cmake/CMakeCargo.cmake @@ -0,0 +1,97 @@ +function(cargo_set_lib_target LIB_NAME) + if(WIN32) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(LIB_TARGET "x86_64-pc-windows-msvc" PARENT_SCOPE) + else() + set(LIB_TARGET "i686-pc-windows-msvc" PARENT_SCOPE) + endif() + elseif(ANDROID) + if(ANDROID_SYSROOT_ABI STREQUAL "x86") + set(LIB_TARGET "i686-linux-android" PARENT_SCOPE) + elseif(ANDROID_SYSROOT_ABI STREQUAL "x86_64") + set(LIB_TARGET "x86_64-linux-android" PARENT_SCOPE) + elseif(ANDROID_SYSROOT_ABI STREQUAL "arm") + set(LIB_TARGET "arm-linux-androideabi" PARENT_SCOPE) + elseif(ANDROID_SYSROOT_ABI STREQUAL "arm64") + set(LIB_TARGET "aarch64-linux-android" PARENT_SCOPE) + endif() + elseif(IOS) + set(LIB_TARGET "universal" PARENT_SCOPE) + elseif(CMAKE_SYSTEM_NAME STREQUAL Darwin) + set(LIB_TARGET "x86_64-apple-darwin" PARENT_SCOPE) + else() + if(RUST_USE_MUSL) + set(RUST_LIBC_NAME "musl") + else() + set(RUST_LIBC_NAME "gnu") + endif() + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(LIB_TARGET "x86_64-unknown-linux-${RUST_LIBC_NAME}" PARENT_SCOPE) + else() + set(LIB_TARGET "i686-unknown-linux-${RUST_LIBC_NAME}" PARENT_SCOPE) + endif() + endif() +endfunction() + +function(cargo_build) + cmake_parse_arguments(CARGO "" "NAME" "" ${ARGN}) + string(REPLACE "-" "_" LIB_NAME ${CARGO_NAME}) + + get_filename_component(CARGO_TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}" ABSOLUTE) + + cargo_set_lib_target(LIB_NAME) + + if(NOT CMAKE_BUILD_TYPE) + set(LIB_BUILD_TYPE "debug") + elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release") + set(LIB_BUILD_TYPE "release") + else() + set(LIB_BUILD_TYPE "debug") + endif() + + set(SHARED_LIB_FNAME "${CMAKE_SHARED_LIBRARY_PREFIX}${LIB_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}") + set(STATIC_LIB_FNAME "${CMAKE_STATIC_LIBRARY_PREFIX}${LIB_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX}") + + set(LIB_BASE_DIR "${CARGO_TARGET_DIR}/${LIB_TARGET}/${LIB_BUILD_TYPE}") + + get_filename_component(SHARED_LIB_FILE "${LIB_BASE_DIR}/${SHARED_LIB_FNAME}" ABSOLUTE) + get_filename_component(STATIC_LIB_FILE "${LIB_BASE_DIR}/${STATIC_LIB_FNAME}" ABSOLUTE) + + if(IOS) + set(CARGO_ARGS "lipo") + else() + set(CARGO_ARGS "build") + list(APPEND CARGO_ARGS "--target" ${LIB_TARGET}) + endif() + + if(${LIB_BUILD_TYPE} STREQUAL "release") + list(APPEND CARGO_ARGS "--release") + endif() + + file(GLOB_RECURSE LIB_SOURCES "*.rs") + + set(CARGO_ENV_COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${CARGO_TARGET_DIR}") + + add_custom_command( + OUTPUT ${STATIC_LIB_FILE} + COMMAND ${CARGO_ENV_COMMAND} ${CARGO_EXECUTABLE} ARGS ${CARGO_ARGS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${LIB_SOURCES} + COMMENT "running cargo") + add_custom_target(${CARGO_NAME}_static_target ALL DEPENDS ${STATIC_LIB_FILE}) + add_library(${CARGO_NAME}_static STATIC IMPORTED GLOBAL) + add_dependencies(${CARGO_NAME}_static ${CARGO_NAME}_static_target) + set_target_properties(${CARGO_NAME}_static PROPERTIES IMPORTED_LOCATION ${STATIC_LIB_FILE}) + + add_custom_command( + OUTPUT ${SHARED_LIB_FILE} + COMMAND ${CARGO_ENV_COMMAND} ${CARGO_EXECUTABLE} ARGS ${CARGO_ARGS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${LIB_SOURCES} + COMMENT "running cargo") + add_custom_target(${CARGO_NAME}_shared_target ALL DEPENDS ${SHARED_LIB_FILE}) + add_library(${CARGO_NAME}_shared SHARED IMPORTED GLOBAL) + add_dependencies(${CARGO_NAME}_shared ${CARGO_NAME}_shared_target) + set_target_properties(${CARGO_NAME}_shared PROPERTIES IMPORTED_LOCATION ${SHARED_LIB_FILE}) +endfunction() diff --git a/cmake/CMakeDetermineRustCompiler.cmake b/cmake/CMakeDetermineRustCompiler.cmake new file mode 100644 index 0000000..33a459c --- /dev/null +++ b/cmake/CMakeDetermineRustCompiler.cmake @@ -0,0 +1,25 @@ + +if(NOT CMAKE_Rust_COMPILER) + find_package(Rust) + if(RUST_FOUND) + set(CMAKE_Rust_COMPILER "${RUSTC_EXECUTABLE}") + set(CMAKE_Rust_COMPILER_ID "Rust") + set(CMAKE_Rust_COMPILER_VERSION "${RUST_VERSION}") + set(CMAKE_Rust_PLATFORM_ID "Rust") + endif() +endif() + +message(STATUS "Cargo Prefix: ${CARGO_PREFIX}") +message(STATUS "Rust Compiler Version: ${RUSTC_VERSION}") + +mark_as_advanced(CMAKE_Rust_COMPILER) + +if(CMAKE_Rust_COMPILER) + set(CMAKE_Rust_COMPILER_LOADED 1) +endif(CMAKE_Rust_COMPILER) + +configure_file(${CMAKE_SOURCE_DIR}/cmake/CMakeRustCompiler.cmake.in + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${CMAKE_VERSION}/CMakeRustCompiler.cmake IMMEDIATE @ONLY) + +set(CMAKE_Rust_COMPILER_ENV_VAR "RUSTC") + diff --git a/cmake/CMakeRustCompiler.cmake.in b/cmake/CMakeRustCompiler.cmake.in new file mode 100644 index 0000000..5916c1c --- /dev/null +++ b/cmake/CMakeRustCompiler.cmake.in @@ -0,0 +1,15 @@ + +set(CMAKE_Rust_COMPILER "@CMAKE_Rust_COMPILER@") +set(CMAKE_Rust_COMPILER_ID "@CMAKE_Rust_COMPILER_ID@") +set(CMAKE_Rust_COMPILER_VERSION "@CMAKE_Rust_COMPILER_VERSION@") +set(CMAKE_Rust_COMPILER_LOADED @CMAKE_Rust_COMPILER_LOADED@) +set(CMAKE_Rust_PLATFORM_ID "@CMAKE_Rust_PLATFORM_ID@") + +SET(CMAKE_Rust_SOURCE_FILE_EXTENSIONS rs) +SET(CMAKE_Rust_LINKER_PREFERENCE 40) +#SET(CMAKE_Rust_OUTPUT_EXTENSION_REPLACE 1) +SET(CMAKE_STATIC_LIBRARY_PREFIX_Rust "") +SET(CMAKE_STATIC_LIBRARY_SUFFIX_Rust .a) + +set(CMAKE_Rust_COMPILER_ENV_VAR "RUSTC") + diff --git a/cmake/CMakeRustInformation.cmake b/cmake/CMakeRustInformation.cmake new file mode 100644 index 0000000..05d96c9 --- /dev/null +++ b/cmake/CMakeRustInformation.cmake @@ -0,0 +1,106 @@ + +# +# Usage: rustc [OPTIONS] INPUT +# +# Options: +# -h --help Display this message +# --cfg SPEC Configure the compilation environment +# -L [KIND=]PATH Add a directory to the library search path. The +# optional KIND can be one of dependency, crate, native, +# framework or all (the default). +# -l [KIND=]NAME Link the generated crate(s) to the specified native +# library NAME. The optional KIND can be one of static, +# dylib, or framework. If omitted, dylib is assumed. +# --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|metadata] +# Comma separated list of types of crates for the +# compiler to emit +# --crate-name NAME Specify the name of the crate being built +# --emit [asm|llvm-bc|llvm-ir|obj|link|dep-info] +# Comma separated list of types of output for the +# compiler to emit +# --print [crate-name|file-names|sysroot|cfg|target-list|target-cpus|target-features|relocation-models|code-models] +# Comma separated list of compiler information to print +# on stdout +# -g Equivalent to -C debuginfo=2 +# -O Equivalent to -C opt-level=2 +# -o FILENAME Write output to +# --out-dir DIR Write output to compiler-chosen filename in +# --explain OPT Provide a detailed explanation of an error message +# --test Build a test harness +# --target TARGET Target triple for which the code is compiled +# -W --warn OPT Set lint warnings +# -A --allow OPT Set lint allowed +# -D --deny OPT Set lint denied +# -F --forbid OPT Set lint forbidden +# --cap-lints LEVEL Set the most restrictive lint level. More restrictive +# lints are capped at this level +# -C --codegen OPT[=VALUE] +# Set a codegen option +# -V --version Print version info and exit +# -v --verbose Use verbose output +# +# Additional help: +# -C help Print codegen options +# -W help Print 'lint' options and default settings +# -Z help Print internal options for debugging rustc +# --help -v Print the full set of options rustc accepts +# + +# + +include(CMakeLanguageInformation) + +if(UNIX) + set(CMAKE_Rust_OUTPUT_EXTENSION .o) +else() + set(CMAKE_Rust_OUTPUT_EXTENSION .obj) +endif() + +set(CMAKE_Rust_ECHO_ALL "echo \"TARGET: TARGET_BASE: ") +set(CMAKE_Rust_ECHO_ALL "${CMAKE_Rust_ECHO_ALL} OBJECT: OBJECTS: OBJECT_DIR: SOURCE: SOURCES: ") +set(CMAKE_Rust_ECHO_ALL "${CMAKE_Rust_ECHO_ALL} LINK_LIBRARIES: FLAGS: LINK_FLAGS: \"") + +if(NOT CMAKE_Rust_CREATE_SHARED_LIBRARY) + set(CMAKE_Rust_CREATE_SHARED_LIBRARY + "echo \"CMAKE_Rust_CREATE_SHARED_LIBRARY\"" + "${CMAKE_Rust_ECHO_ALL}" + ) +endif() + +if(NOT CMAKE_Rust_CREATE_SHARED_MODULE) + set(CMAKE_Rust_CREATE_SHARED_MODULE + "echo \"CMAKE_Rust_CREATE_SHARED_MODULE\"" + "${CMAKE_Rust_ECHO_ALL}" + ) +endif() + +if(NOT CMAKE_Rust_CREATE_STATIC_LIBRARY) + set(CMAKE_Rust_CREATE_STATIC_LIBRARY + "echo \"CMAKE_Rust_CREATE_STATIC_LIBRARY\"" + "${CMAKE_Rust_ECHO_ALL}" + ) +endif() + +if(NOT CMAKE_Rust_COMPILE_OBJECT) + set(CMAKE_Rust_COMPILE_OBJECT + "echo \"CMAKE_Rust_COMPILE_OBJECT\"" + "${CMAKE_Rust_ECHO_ALL}" + "${CMAKE_Rust_COMPILER} --emit obj -o ") +endif() + +if(NOT CMAKE_Rust_LINK_EXECUTABLE) + set(CMAKE_Rust_LINK_EXECUTABLE + "echo \"CMAKE_Rust_LINK_EXECUTABLE\"" + "${CMAKE_Rust_ECHO_ALL}" + ) +endif() + +mark_as_advanced( + CMAKE_Rust_FLAGS + CMAKE_Rust_FLAGS_DEBUG + CMAKE_Rust_FLAGS_MINSIZEREL + CMAKE_Rust_FLAGS_RELEASE + CMAKE_Rust_FLAGS_RELWITHDEBINFO) + +set(CMAKE_Rust_INFORMATION_LOADED 1) + diff --git a/cmake/CMakeTestRustCompiler.cmake b/cmake/CMakeTestRustCompiler.cmake new file mode 100644 index 0000000..ff75bb3 --- /dev/null +++ b/cmake/CMakeTestRustCompiler.cmake @@ -0,0 +1,3 @@ + +set(CMAKE_Rust_COMPILER_WORKS 1 CACHE INTERNAL "") + diff --git a/cmake/FindRust.cmake b/cmake/FindRust.cmake new file mode 100644 index 0000000..2dbf14d --- /dev/null +++ b/cmake/FindRust.cmake @@ -0,0 +1,75 @@ + +set(_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ${CMAKE_FIND_ROOT_PATH_MODE_PROGRAM}) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(_CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ${CMAKE_FIND_ROOT_PATH_MODE_INCLUDE}) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) + +if(WIN32) + set(USER_HOME "$ENV{USERPROFILE}") +else() + set(USER_HOME "$ENV{HOME}") +endif() + +# Find cargo prefix +find_path(CARGO_PREFIX ".cargo" + PATHS "${USER_HOME}") + +if(CARGO_PREFIX MATCHES "NOTFOUND") + message(FATAL_ERROR "Could not find Rust!") +else() + set(CARGO_PREFIX "${CARGO_PREFIX}/.cargo") +endif() + +# Find cargo executable +find_program(CARGO_EXECUTABLE cargo + HINTS "${CARGO_PREFIX}" + PATH_SUFFIXES "bin") +mark_as_advanced(CARGO_EXECUTABLE) + +# Find rustc executable +find_program(RUSTC_EXECUTABLE rustc + HINTS "${CARGO_PREFIX}" + PATH_SUFFIXES "bin") +mark_as_advanced(RUSTC_EXECUTABLE) + +# Find rustdoc executable +find_program(RUSTDOC_EXECUTABLE rustdoc + HINTS "${CARGO_PREFIX}" + PATH_SUFFIXES "bin") +mark_as_advanced(RUSTDOC_EXECUTABLE) + +# Find rust-gdb executable +find_program(RUST_GDB_EXECUTABLE rust-gdb + HINTS "${CARGO_PREFIX}" + PATH_SUFFIXES "bin") +mark_as_advanced(RUST_GDB_EXECUTABLE) + +# Find rust-lldb executable +find_program(RUST_LLDB_EXECUTABLE rust-lldb + HINTS "${CARGO_PREFIX}" + PATH_SUFFIXES "bin") +mark_as_advanced(RUST_LLDB_EXECUTABLE) + +# Find rustup executable +find_program(RUSTUP_EXECUTABLE rustup + HINTS "${CARGO_PREFIX}" + PATH_SUFFIXES "bin") +mark_as_advanced(RUSTUP_EXECUTABLE) + +set(RUST_FOUND FALSE CACHE INTERNAL "") + +if(CARGO_EXECUTABLE AND RUSTC_EXECUTABLE AND RUSTDOC_EXECUTABLE) + set(RUST_FOUND TRUE CACHE INTERNAL "") + + set(CARGO_PREFIX "${CARGO_PREFIX}" CACHE PATH "Rust Cargo prefix") + + execute_process(COMMAND ${RUSTC_EXECUTABLE} --version OUTPUT_VARIABLE RUSTC_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) + string(REGEX REPLACE "rustc ([^ ]+) .*" "\\1" RUSTC_VERSION "${RUSTC_VERSION}") +endif() + +if(NOT RUST_FOUND) + message(FATAL_ERROR "Could not find Rust!") +endif() + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ${_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM}) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ${_CMAKE_FIND_ROOT_PATH_MODE_INCLUDE})