diff options
Diffstat (limited to 'cmake/mason.cmake')
-rw-r--r-- | cmake/mason.cmake | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/cmake/mason.cmake b/cmake/mason.cmake new file mode 100644 index 0000000000..4e4e46b619 --- /dev/null +++ b/cmake/mason.cmake @@ -0,0 +1,212 @@ +# Mason CMake + +include(CMakeParseArguments) + +function(mason_detect_platform) + # Determine platform + if(NOT MASON_PLATFORM) + # we call uname -s manually here since + # CMAKE_HOST_SYSTEM_NAME will not be defined before the project() call + execute_process( + COMMAND uname -s + OUTPUT_VARIABLE UNAME + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if (UNAME STREQUAL "Darwin") + set(MASON_PLATFORM "osx" PARENT_SCOPE) + else() + set(MASON_PLATFORM "linux" PARENT_SCOPE) + endif() + endif() + + # Determine platform version string + if(NOT MASON_PLATFORM_VERSION) + execute_process( + COMMAND uname -m + OUTPUT_VARIABLE MASON_PLATFORM_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(MASON_PLATFORM_VERSION "${MASON_PLATFORM_VERSION}" PARENT_SCOPE) + endif() +endfunction() + +function(mason_use _PACKAGE) + if(NOT _PACKAGE) + message(FATAL_ERROR "[Mason] No package name given") + endif() + + cmake_parse_arguments("" "HEADER_ONLY" "VERSION" "" ${ARGN}) + + if(_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "[Mason] mason_use() called with unrecognized arguments: ${_UNPARSED_ARGUMENTS}") + endif() + + if(NOT _VERSION) + message(FATAL_ERROR "[Mason] Specifying a version is required") + endif() + + if(MASON_PACKAGE_${_PACKAGE}_INVOCATION STREQUAL "${MASON_INVOCATION}") + # Check that the previous invocation of mason_use didn't select another version of this package + if(NOT MASON_PACKAGE_${_PACKAGE}_VERSION STREQUAL ${_VERSION}) + message(FATAL_ERROR "[Mason] Already using ${_PACKAGE} ${MASON_PACKAGE_${_PACKAGE}_VERSION}. Cannot select version ${_VERSION}.") + endif() + else() + if(_HEADER_ONLY) + set(_PLATFORM_ID "headers") + else() + set(_PLATFORM_ID "${MASON_PLATFORM}-${MASON_PLATFORM_VERSION}") + endif() + + set(_SLUG "${_PLATFORM_ID}/${_PACKAGE}/${_VERSION}") + set(_INSTALL_PATH "${MASON_PACKAGE_DIR}/${_SLUG}") + file(RELATIVE_PATH _INSTALL_PATH_RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${_INSTALL_PATH}") + + if(NOT EXISTS "${_INSTALL_PATH}") + set(_CACHE_PATH "${MASON_PACKAGE_DIR}/.binaries/${_SLUG}.tar.gz") + if (NOT EXISTS "${_CACHE_PATH}") + # Download the package + set(_URL "${MASON_REPOSITORY}/${_SLUG}.tar.gz") + message("[Mason] Downloading package ${_URL}...") + + set(_FAILED) + set(_ERROR) + # Note: some CMake versions are compiled without SSL support + get_filename_component(_CACHE_DIR "${_CACHE_PATH}" DIRECTORY) + file(MAKE_DIRECTORY "${_CACHE_DIR}") + execute_process( + COMMAND curl --retry 3 -s -f -S -L "${_URL}" -o "${_CACHE_PATH}.tmp" + RESULT_VARIABLE _FAILED + ERROR_VARIABLE _ERROR) + if(_FAILED) + message(FATAL_ERROR "[Mason] Failed to download ${_URL}: ${_ERROR}") + else() + # We downloaded to a temporary file to prevent half-finished downloads + file(RENAME "${_CACHE_PATH}.tmp" "${_CACHE_PATH}") + endif() + endif() + + # Unpack the package + message("[Mason] Unpacking package to ${_INSTALL_PATH_RELATIVE}...") + file(MAKE_DIRECTORY "${_INSTALL_PATH}") + execute_process( + COMMAND ${CMAKE_COMMAND} -E tar xzf "${_CACHE_PATH}" + WORKING_DIRECTORY "${_INSTALL_PATH}") + endif() + + # Error out if there is no config file. + if(NOT EXISTS "${_INSTALL_PATH}/mason.ini") + message(FATAL_ERROR "[Mason] Could not find mason.ini for package ${_PACKAGE} ${_VERSION}") + endif() + + set(MASON_PACKAGE_${_PACKAGE}_PREFIX "${_INSTALL_PATH}" CACHE STRING "${_PACKAGE} ${_INSTALL_PATH}" FORCE) + mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_PREFIX) + + # Load the configuration from the ini file + file(STRINGS "${_INSTALL_PATH}/mason.ini" _CONFIG_FILE) + foreach(_LINE IN LISTS _CONFIG_FILE) + string(REGEX MATCH "^([a-z_]+) *= *" _KEY "${_LINE}") + if (_KEY) + string(LENGTH "${_KEY}" _KEY_LENGTH) + string(SUBSTRING "${_LINE}" ${_KEY_LENGTH} -1 _VALUE) + string(REGEX REPLACE ";.*$" "" _VALUE "${_VALUE}") # Trim trailing commas + string(REPLACE "{prefix}" "${_INSTALL_PATH}" _VALUE "${_VALUE}") + string(STRIP "${_VALUE}" _VALUE) + string(REPLACE "=" "" _KEY "${_KEY}") + string(STRIP "${_KEY}" _KEY) + string(TOUPPER "${_KEY}" _KEY) + if(_KEY STREQUAL "INCLUDE_DIRS" OR _KEY STREQUAL "STATIC_LIBS" ) + separate_arguments(_VALUE) + endif() + set(MASON_PACKAGE_${_PACKAGE}_${_KEY} "${_VALUE}" CACHE STRING "${_PACKAGE} ${_KEY}" FORCE) + mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_${_KEY}) + endif() + endforeach() + + # Compare version in the package to catch errors early on + if(NOT _VERSION STREQUAL MASON_PACKAGE_${_PACKAGE}_VERSION) + message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has version '${MASON_PACKAGE_${_PACKAGE}_VERSION}', but required '${_VERSION}'") + endif() + + if(NOT _PACKAGE STREQUAL MASON_PACKAGE_${_PACKAGE}_NAME) + message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has name '${MASON_PACKAGE_${_PACKAGE}_NAME}', but required '${_NAME}'") + endif() + + if(NOT _HEADER_ONLY) + if(NOT MASON_PLATFORM STREQUAL MASON_PACKAGE_${_PACKAGE}_PLATFORM) + message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has platform '${MASON_PACKAGE_${_PACKAGE}_PLATFORM}', but required '${MASON_PLATFORM}'") + endif() + + if(NOT MASON_PLATFORM_VERSION STREQUAL MASON_PACKAGE_${_PACKAGE}_PLATFORM_VERSION) + message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has platform version '${MASON_PACKAGE_${_PACKAGE}_PLATFORM_VERSION}', but required '${MASON_PLATFORM_VERSION}'") + endif() + endif() + + # Concatenate the static libs and libraries + set(_LIBRARIES) + list(APPEND _LIBRARIES ${MASON_PACKAGE_${_PACKAGE}_STATIC_LIBS} ${MASON_PACKAGE_${_PACKAGE}_LDFLAGS}) + set(MASON_PACKAGE_${_PACKAGE}_LIBRARIES "${_LIBRARIES}" CACHE STRING "${_PACKAGE} _LIBRARIES" FORCE) + mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_LIBRARIES) + + if(NOT _HEADER_ONLY) + string(REGEX MATCHALL "(^| +)-L *([^ ]+)" MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LDFLAGS}") + string(REGEX REPLACE "(^| +)-L *" "\\1" MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}") + set(MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}" CACHE STRING "${_PACKAGE} ${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}" FORCE) + mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS) + endif() + + # Store invocation ID to prevent different versions of the same package in one invocation + set(MASON_PACKAGE_${_PACKAGE}_INVOCATION "${MASON_INVOCATION}" CACHE INTERNAL "${_PACKAGE} invocation ID" FORCE) + endif() +endfunction() + +macro(target_add_mason_package _TARGET _VISIBILITY _PACKAGE) + if (NOT MASON_PACKAGE_${_PACKAGE}_INVOCATION) + message(FATAL_ERROR "[Mason] Package ${_PACKAGE} has not been initialized yet") + endif() + + target_include_directories(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_INCLUDE_DIRS}") + target_compile_definitions(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_DEFINITIONS}") + target_compile_options(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_OPTIONS}") + target_link_libraries(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_LIBRARIES}") +endmacro() + +# Setup + +string(RANDOM LENGTH 16 MASON_INVOCATION) + +# Read environment variables if CMake is run in command mode +if (CMAKE_ARGC) + set(MASON_PLATFORM "$ENV{MASON_PLATFORM}") + set(MASON_PLATFORM_VERSION "$ENV{MASON_PLATFORM_VERSION}") + set(MASON_PACKAGE_DIR "$ENV{MASON_PACKAGE_DIR}") + set(MASON_REPOSITORY "$ENV{MASON_REPOSITORY}") +endif() + +# Directory where Mason packages are located; typically ends with mason_packages +if (NOT MASON_PACKAGE_DIR) + set(MASON_PACKAGE_DIR "${CMAKE_SOURCE_DIR}/mason_packages") +endif() + +# URL prefix of where packages are located. +if (NOT MASON_REPOSITORY) + set(MASON_REPOSITORY "https://mason-binaries.s3.amazonaws.com") +endif() + +mason_detect_platform() + +# Execute commands if CMake is run in command mode +if (CMAKE_ARGC) + # Collect remaining arguments for passing to mason_use + set(_MASON_ARGS) + foreach(I RANGE 4 ${CMAKE_ARGC}) + list(APPEND _MASON_ARGS "${CMAKE_ARGV${I}}") + endforeach() + + # Install the package + mason_use(${_MASON_ARGS}) + + # Optionally print variables + if(DEFINED MASON_PACKAGE_${CMAKE_ARGV4}_${CMAKE_ARGV3}) + # CMake can't write to stdout with message() + execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${MASON_PACKAGE_${CMAKE_ARGV4}_${CMAKE_ARGV3}}") + endif() +endif() |