cesium-native/cmake/ezvcpkg/ezvcpkg.cmake

307 lines
12 KiB
CMake

#
# EZVCPKG: A CMake function for installing VCPKG dependencies
# Used under Apache 2.0 license
# Created by Bradley Austin Davis on 2019/07/28
# Website: https://github.com/jherico/ezvcpkg
#
# Arguments:
# PACKAGES a multi-value argument specifying the packages to install
# REPO The github repository organization and name, defaults to "microsoft/vcpkg"
# URL the URL of the git repository. Defaults to "https://github.com/${REPO}.git"
# COMMIT The commit ID of the git repository to select when building. Defaults to "f990dfaa5ba82155f95b75021453c075816fd4be"
# UPDATE_TOOLCHAIN if this flag is set, the function will populate the CMAKE_TOOLCHAIN_FILE variable. Note that this will
# only have an effect if done prior to the `project()` directive of the top level CMake project.
# BASEDIR The directory in which CMake will checkout and build vcpkg instances. Each vcpkg commit will have it's own
# subdirectory. If this is not specified, it will default to the location specified by the EZVCPKG_BASEDIR environment
# variable. If that doesn't exist, it will default to ~/.ezvcpkg on OSX, and %TEMP%/ezvcpkg everywhere else
# CLEAN a single argument parameter that tells the script to clean up out of date EZVCPKG folders. The value should be the
# number of days after which a folder should be removed if it hasn't been touched. (Not currently implemented)
#
# Output:
# If successful the function will populate EZVCPKG_DIR with the location of the installed triplet, for instance
# D:\ezvcpkg\f990dfaa5ba82155f95b75021453c075816fd4be\installed\x64-windows
set(EZVCPKG_VERSION 0.1)
macro(EZVCPKG_CALCULATE_PATHS)
if (NOT EZVCPKG_BASEDIR)
if (DEFINED ENV{EZVCPKG_BASEDIR})
set(EZVCPKG_BASEDIR $ENV{EZVCPKG_BASEDIR})
else()
if(APPLE)
# OSX wipes binaries from the temp directory over time, leaving the remaining files
# This causes the vcpkg folder to end up in an unusable state, so we default to the
# home directory instead
set(EZVCPKG_BASEDIR "$ENV{HOME}/.ezvcpkg")
else()
# Initially defaulted to $ENV{TEMP} but the github build hosts don't populate this
set(EZVCPKG_BASEDIR "$ENV{HOME}/.ezvcpkg")
endif()
# We want people to specify a base directory, either through the calling EZVCPKG_FETCH
# function or through an environment variable.
message(STATUS "EZVCPKG_BASEDIR envrionment variable not found and basedir not set, using default ${EZVCPKG_BASEDIR}")
endif()
endif()
file(TO_CMAKE_PATH "${EZVCPKG_BASEDIR}/${EZVCPKG_COMMIT}" EZVCPKG_DIR)
file(TO_CMAKE_PATH "${EZVCPKG_BASEDIR}/${EZVCPKG_COMMIT}.lock" EZVCPKG_LOCK)
if (EZVCPKG_USE_HOST_VCPKG)
find_program(EZVCPKG_HOST_VCPKG vcpkg)
if (EZVCPKG_HOST_VCPKG)
message(STATUS "EZVCPKG using host vcpkg binary ${EZVCPKG_HOST_VCPKG}")
endif()
endif()
if (CMAKE_HOST_WIN32)
file(TO_CMAKE_PATH "${EZVCPKG_DIR}/vcpkg.exe" EZVCPKG_EXE)
file(TO_CMAKE_PATH "${EZVCPKG_DIR}/bootstrap-vcpkg.bat" EZVCPKG_BOOTSTRAP)
else()
file(TO_CMAKE_PATH "${EZVCPKG_DIR}/vcpkg" EZVCPKG_EXE)
file(TO_CMAKE_PATH "${EZVCPKG_DIR}/bootstrap-vcpkg.sh" EZVCPKG_BOOTSTRAP)
endif()
# The tag file exists purely to be a touch target every time the ezvcpkg macro is called
# making it easy to find out of date ezvcpkg folder.
file(TO_CMAKE_PATH "${EZVCPKG_DIR}/.tag" EZVCPKG_TAG)
file(TO_CMAKE_PATH "${EZVCPKG_DIR}/README.md" EZVCPKG_README)
if (NOT VCPKG_TRIPLET)
message(FATAL_ERROR "VCPKG_TRIPLET must be set")
endif()
file(TO_CMAKE_PATH "${EZVCPKG_DIR}/installed/${VCPKG_TRIPLET}" EZVCPKG_INSTALLED_DIR)
file(TO_CMAKE_PATH "${EZVCPKG_DIR}/scripts/buildsystems/vcpkg.cmake" EZVCPKG_CMAKE_TOOLCHAIN)
endmacro()
macro(EZVCPKG_CHECK_RESULTS)
if (NOT (EZVCPKG_RESULT EQUAL 0))
if (EZVCPKG_IGNORE_ERRORS)
message(WARNING "EZVCPKG failed with error ${EZVCPKG_RESULT}\n${EZVCPKG_OUTPUT}")
return()
else()
message(SEND_ERROR "EZVCPKG failed with error ${EZVCPKG_RESULT}")
message("*** The output from the command was:")
message("${EZVCPKG_OUTPUT}")
if (EZVCPKG_OUTPUT MATCHES "See logs for more information:\r?\n([^\r\n]+\r?\n)*")
string(REGEX MATCHALL "\r?\n([^\r\n]+)" LOGS "${CMAKE_MATCH_0}")
foreach(LOG IN LISTS LOGS)
string(STRIP "${LOG}" LOG_FILENAME)
if(EXISTS "${LOG_FILENAME}")
message("*** Content of log file: ${LOG_FILENAME}:")
file(READ "${LOG_FILENAME}" LOG_CONTENT)
message("${LOG_CONTENT}")
else()
message("*** Could not find log file: ${LOG_FILENAME}")
endif()
endforeach()
endif()
message(FATAL_ERROR)
endif()
endif()
endmacro()
macro(EZVCPKG_BOOTSTRAP)
if (NOT EXISTS "${EZVCPKG_README}")
message(STATUS "EZVCPKG Bootstrapping")
find_package(Git)
if (NOT Git_FOUND)
message(FATAL_ERROR "EZVCPKG Git not found, can't bootstrap vcpkg")
endif()
message(STATUS "EZVCPKG Cloning repository")
execute_process(
COMMAND "${GIT_EXECUTABLE}" "clone" ${EZVCPKG_URL} "${EZVCPKG_DIR}"
RESULTS_VARIABLE EZVCPKG_RESULT
OUTPUT_VARIABLE EZVCPKG_OUTPUT
ERROR_VARIABLE EZVCPKG_OUTPUT
)
EZVCPKG_CHECK_RESULTS()
# FIXME put this into a separate check verifying the commit ID of the current directory
# If the previous run had the clone work, but the checkout fail, the readme will be
# present and the user will have the default checkout, rather than their requested commit
message(STATUS "EZVCPKG Checking out commit ${EZVCPKG_COMMIT}")
execute_process(
COMMAND "${GIT_EXECUTABLE}" "-c" "advice.detachedHead=false" "checkout" ${EZVCPKG_COMMIT}
WORKING_DIRECTORY "${EZVCPKG_DIR}"
RESULTS_VARIABLE EZVCPKG_RESULT
OUTPUT_VARIABLE EZVCPKG_OUTPUT
ERROR_VARIABLE EZVCPKG_OUTPUT
)
EZVCPKG_CHECK_RESULTS()
endif()
if (EZVCPKG_HOST_VCPKG)
set(EZVCPKG_EXE "${EZVCPKG_HOST_VCPKG}")
elseif(NOT EXISTS "${EZVCPKG_EXE}")
message(STATUS "EZVCPKG Bootstrapping vcpkg binary")
message(STATUS "EZVCPKG vcpkg bootstrap command: ${EZVCPKG_BOOTSTRAP}")
message(STATUS "EZVCPKG vcpkg working dir: ${EZVCPKG_DIR}")
execute_process(
COMMAND ${EZVCPKG_BOOTSTRAP}
WORKING_DIRECTORY "${EZVCPKG_DIR}"
RESULTS_VARIABLE EZVCPKG_RESULT
OUTPUT_VARIABLE EZVCPKG_OUTPUT
ERROR_VARIABLE EZVCPKG_OUTPUT
)
EZVCPKG_CHECK_RESULTS()
endif()
if (NOT EXISTS "${EZVCPKG_EXE}")
if (EZVCPKG_IGNORE_ERRORS)
message(WARNING "EZVCPKG vcpkg binary not failed")
return()
else()
message(FATAL_ERROR "EZVCPKG vcpkg binary not found")
endif()
endif()
endmacro()
macro(EZVCPKG_BUILD)
set(INSTALL_COMMAND "${EZVCPKG_EXE}" --vcpkg-root "${EZVCPKG_DIR}" install --triplet ${VCPKG_TRIPLET})
if (DEFINED VCPKG_OVERLAY_PORTS)
if (CMAKE_HOST_WIN32)
set(ENV{VCPKG_OVERLAY_PORTS} "${VCPKG_OVERLAY_PORTS}")
else()
string(REPLACE ";" ":" VCPKG_OVERLAY_PORTS_POSIX "${VCPKG_OVERLAY_PORTS}")
set(ENV{VCPKG_OVERLAY_PORTS} "${VCPKG_OVERLAY_PORTS_POSIX}")
endif()
endif()
if (DEFINED VCPKG_OVERLAY_TRIPLETS)
if (CMAKE_HOST_WIN32)
set(ENV{VCPKG_OVERLAY_TRIPLETS} "${VCPKG_OVERLAY_TRIPLETS}")
else()
string(REPLACE ";" ":" VCPKG_OVERLAY_TRIPLETS_POSIX "${VCPKG_OVERLAY_TRIPLETS}")
set(ENV{VCPKG_OVERLAY_TRIPLETS} "${VCPKG_OVERLAY_TRIPLETS_POSIX}")
endif()
endif()
if (EZVCPKG_SERIALIZE)
foreach(_PACKAGE ${EZVCPKG_PACKAGES})
message(STATUS "EZVCPKG Building/Verifying package ${_PACKAGE} using triplet ${VCPKG_TRIPLET}")
execute_process(
COMMAND ${INSTALL_COMMAND} ${_PACKAGE}
WORKING_DIRECTORY "${EZVCPKG_DIR}"
RESULTS_VARIABLE EZVCPKG_RESULT
OUTPUT_VARIABLE EZVCPKG_OUTPUT
ERROR_VARIABLE EZVCPKG_OUTPUT
)
EZVCPKG_CHECK_RESULTS()
endforeach()
else()
message(STATUS "EZVCPKG Building/Verifying packages ${EZVCPKG_PACKAGES} using triplet ${VCPKG_TRIPLET}")
execute_process(
COMMAND ${INSTALL_COMMAND} ${EZVCPKG_PACKAGES}
WORKING_DIRECTORY "${EZVCPKG_DIR}"
RESULTS_VARIABLE EZVCPKG_RESULT
OUTPUT_VARIABLE EZVCPKG_OUTPUT
ERROR_VARIABLE EZVCPKG_OUTPUT
)
EZVCPKG_CHECK_RESULTS()
endif()
# if we didn't blow up, wipe the build trees to avoid huge cache usage on CI servers
if (EZVCPKG_CLEAN_BUILDTREES)
file(TO_CMAKE_PATH "${EZVCPKG_DIR}/buildtrees" EZVCPKG_BUILDTREES)
if (EXISTS ${EZVCPKG_BUILDTREES})
file(REMOVE_RECURSE "${EZVCPKG_DIR}/buildtrees")
endif()
endif()
endmacro()
macro(EZVCPKG_CLEAN)
message(STATUS "EZVCPKG Cleaning unused ezvcpkg dirs after ${EZVCPKG_CLEAN} days")
message(WARNING "Cleaning not implemented")
endmacro()
macro(EZVCPKG_CHECK_ARGS)
# Default to a recent vcpkg commit
if (NOT EZVCPKG_COMMIT)
set(EZVCPKG_COMMIT "501db0f17ef6df184fcdbfbe0f87cde2313b6ab1")
endif()
if (EZVCPKG_REPO AND EZVCPKG_URL)
message(FATAL_ERROR "Set either a Git repository URL or a REPO org/name combination, but not both")
endif()
# Default to the microsoft root vcpkg repository
if((NOT EZVCPKG_URL) AND (NOT EZVCPKG_REPO))
set(EZVCPKG_REPO "microsoft/vcpkg")
endif()
# Default to github
if(NOT EZVCPKG_URL)
set(EZVCPKG_URL "https://github.com/${EZVCPKG_REPO}.git")
endif()
if (NOT EZVCPKG_PACKAGES)
message(FATAL_ERROR "No vcpkg packages specified")
endif()
endmacro()
function(EZVCPKG_FETCH_IMPL)
if (NOT EZVCPKG_ANNOUNCED)
set(EZVCPKG_ANNOUNCED 1 PARENT_SCOPE)
message(STATUS "EZVCPKG v${EZVCPKG_VERSION} starting up\n\tWebsite: https://github.com/jherico/ezvcpkg")
endif()
set(options UPDATE_TOOLCHAIN SERIALIZE USE_HOST_VCPKG CLEAN_BUILDTREES IGNORE_ERRORS)
set(oneValueArgs COMMIT URL REPO BASEDIR OUTPUT CLEAN)
set(multiValueArgs PACKAGES)
cmake_parse_arguments(EZVCPKG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
# Validate arguments
EZVCPKG_CHECK_ARGS()
# Figure out the paths for everything
EZVCPKG_CALCULATE_PATHS()
message(STATUS "EZVCPKG initializing\n\tcommit: ${EZVCPKG_COMMIT}\n\trepository: ${EZVCPKG_URL}\n\tlocal dir: ${EZVCPKG_DIR}")
# We can't assume build systems will be well behaved if we attempt two different concurrent builds of the same
# target, so we need to have some locking here to ensure that for a given vcpkg location, only one instance
# of vcpkg runs at a given time.
# This is critical because, for instance, when Android Studio does a native code build it will concurrently execute
# cmake for debug and release versions of the build, in different directories, and therefore concurrently try to
# bootstrap and run vcpkg, which will almost certainly corrupt your build directories
file(LOCK ${EZVCPKG_LOCK})
EZVCPKG_BOOTSTRAP()
# Touch the tag file so that this directory is marked as recent, and therefore not cleanable
file(TOUCH ${EZVCPKG_TAG})
# While still holding the global lock, clean up old build directories
if (EZVCPKG_CLEAN)
EZVCPKG_CLEAN()
endif()
# Build packages
EZVCPKG_BUILD()
file(LOCK ${EZVCPKG_LOCK} RELEASE)
file(REMOVE ${EZVCPKG_LOCK})
set(EZVCPKG_PACKAGES_DIR "${EZVCPKG_DIR}/packages" PARENT_SCOPE)
set(EZVCPKG_DIR "${EZVCPKG_INSTALLED_DIR}" PARENT_SCOPE)
if (EZVCPKG_UPDATE_TOOLCHAIN)
set(CMAKE_TOOLCHAIN_FILE ${EZVCPKG_CMAKE_TOOLCHAIN} PARENT_SCOPE)
endif()
message(STATUS "EZVCPKG done")
endfunction()
macro(EZVCPKG_FETCH)
EZVCPKG_FETCH_IMPL(${ARGN})
if (EZVCPKG_QUIT)
unset(EZVCPKG_QUIT CACHE)
message(STATUS "EZVCPKG quitting")
return()
endif()
endmacro()