Created
May 30, 2024 21:50
-
-
Save tresf/0b99944ca5709115cb7895f1059dd9a4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Downloads an executable from the provided URL for use in a build system | |
# and optionally prepends it to the PATH | |
# | |
# Assumes: | |
# - CMAKE_CURRENT_BINARY_DIR/[${_name}] | |
# - CPACK_CURRENT_BINARY_DIR/[${_name}] | |
# - Fallback to $ENV{TMPDIR}/[RANDOM]/[${_name}] | |
# - For verbose, set COMMAND_ECHO to STDOUT in calling script | |
# | |
macro(download_binary RESULT_VARIABLE _url _name _prepend_to_path) | |
if(NOT COMMAND_ECHO OR "${COMMAND_ECHO}" STREQUAL "NONE") | |
set(_command_echo NONE) | |
set(_output_quiet OUTPUT_QUIET) | |
set(_error_quiet ERROR_QUIET) | |
else() | |
set(_command_echo "${COMMAND_ECHO}") | |
set(_output_quiet "") | |
set(_error_quiet "") | |
endif() | |
# Allow special "foo.run --target ./bar" handling for .run files | |
if("${_url}" MATCHES "\\.run$" OR "${_name}" MATCHES "\\.run$") | |
set(_${RESULT_VARIABLE}_USES_MAKESELF true) | |
endif() | |
# Check if fuse is needed | |
if("${RESULT_VARIABLE}" MATCHES "\\.AppImage$" OR "${_name}" MATCHES "\\.AppImage$") | |
message("Checking if FUSE is available to the kernel...") | |
set(_${RESULT_VARIABLE}_NEEDS_FUSE true) | |
endif() | |
# Check if fuse is available | |
if(_${RESULT_VARIABLE}_NEEDS_FUSE) | |
# Ensure we only check on first call | |
if(NOT _FUSE_CHECKED) | |
execute_process(COMMAND fusermount -V | |
${_output_quiet} | |
COMMAND_ECHO ${_command_echo} | |
RESULT_VARIABLE _status_code) | |
set(_FUSED_CHECKED TRUE) | |
if(_status_code EQUAL 0) | |
set(_FUSE_FOUND true) | |
message("FUSE is available") | |
else() | |
message("FUSE is NOT available, we'll extract the AppImage before using") | |
endif() | |
endif() | |
endif() | |
# Determine a suitable working directory | |
if(CMAKE_CURRENT_BINARY_DIR) | |
# Assume we're called from configure step | |
set(_working_dir "${CMAKE_CURRENT_BINARY_DIR}") | |
elseif(CPACK_CURRENT_BINARY_DIR) | |
# Assume cpack (non-standard variable name, but used throughout) | |
set(_working_dir "${CPACK_CURRENT_BINARY_DIR}") | |
else() | |
# Fallback to somewhere temporary, writable | |
if($ENV{_tmpdir}) | |
# POSIX | |
set(_tmpdir "$ENV{_tmpdir}") | |
elseif($ENV{TEMP}) | |
# Windows | |
set(_tmpdir "$ENV{TEMP}") | |
else() | |
# Linux, shame on you! | |
find_program(MKTEMP mktemp) | |
if(MKTEMP) | |
execute_process(COMMAND mktemp | |
OUTPUT_VARIABLE _working_dir | |
OUTPUT_STRIP_TRAILING_WHITESPACE | |
${_output_quiet} | |
COMMAND_ECHO ${_command_echo}) | |
# mktemp formats it how we want it | |
else() | |
# Ummm... Linux you can do better! | |
set(_tmpdir "/tmp") | |
endif() | |
endif() | |
if(NOT DEFINED _working_dir) | |
string(RANDOM subdir) | |
set(_working_dir "${_tmpdir}/tmp.${subdir}") | |
endif() | |
if(NOT EXISTS "${_working_dir}") | |
file(MAKE_DIRECTORY "${_working_dir}") | |
endif() | |
endif() | |
if(_prepend_to_path) | |
# Ensure the PATH is configured | |
string(FIND "$ENV{PATH}" "${_working_dir}" _pathloc) | |
if(NOT $_pathloc EQUAL 0) | |
set(ENV{PATH} "${_working_dir}:$ENV{PATH}") | |
endif() | |
endif() | |
# First ensure the binary doesn't already exist | |
find_program(_${RESULT_VARIABLE} "${_name}" HINTS "${_working_dir}") | |
set(_binary_path "${_working_dir}/${_name}") | |
if(NOT _${RESULT_VARIABLE}) | |
message(STATUS "Downloading ${_name} from ${_url}...") | |
file(DOWNLOAD | |
"${_url}" | |
"${_binary_path}" | |
STATUS DOWNLOAD_STATUS) | |
# Check if download was successful. | |
list(GET DOWNLOAD_STATUS 0 STATUS_CODE) | |
list(GET DOWNLOAD_STATUS 1 ERROR_MESSAGE) | |
if(NOT ${STATUS_CODE} EQUAL 0) | |
file(REMOVE "${_binary_path}") | |
message(FATAL_ERROR "Error downloading ${_url} ${ERROR_MESSAGE}") | |
endif() | |
# Ensure the file is executable | |
file(CHMOD "${_binary_path}" PERMISSIONS | |
OWNER_EXECUTE OWNER_WRITE OWNER_READ | |
GROUP_EXECUTE GROUP_WRITE GROUP_READ) | |
# Ensure it's found | |
find_program(_${RESULT_VARIABLE} "${_name}" HINTS "${_working_dir}" REQUIRED) | |
endif() | |
# We need to create a subdirectory for this binary and symlink it's AppRun to where it's expected | |
if(_${RESULT_VARIABLE}_NEEDS_FUSE AND NOT _FUSE_FOUND) | |
if(NOT COMMAND create_symlink) | |
include(CreateSymlink) | |
endif() | |
# extract appimage | |
execute_process(COMMAND "${_${RESULT_VARIABLE}}" --appimage-extract | |
WORKING_DIRECTORY "${_working_dir}" | |
COMMAND_ECHO ${_command_echo} | |
${_output_quiet} | |
${_error_quiet} | |
COMMAND_ERROR_IS_FATAL ANY) | |
# move extracted files to dedicated location (e.g. ".linuxdeploy-x86_64.AppImage/squashfs-root/") | |
file(MAKE_DIRECTORY "${_working_dir}/.${_name}/") | |
file(RENAME "${_working_dir}/squashfs-root/" "${_working_dir}/.${_name}/squashfs-root/") | |
# remove the unusable binary | |
file(REMOVE "${_${RESULT_VARIABLE}}") | |
# symlink the expected binary name to the AppRun file | |
create_symlink("${_working_dir}/.${_name}/squashfs-root/AppRun" "${_${RESULT_VARIABLE}}") | |
endif() | |
# We need to create a subdirectory for this binary and symlink it's program name to where it's expected | |
if(_${RESULT_VARIABLE}_USES_MAKESELF) | |
if(NOT COMMAND create_symlink) | |
include(CreateSymlink) | |
endif() | |
execute_process(COMMAND "${_${RESULT_VARIABLE}}" | |
--target "${_working_dir}/.${_name}" | |
--nox11 | |
${_output_quiet} | |
COMMAND_ECHO ${_command_echo} | |
${_output_quiet} | |
${_error_quiet} | |
COMMAND_ERROR_IS_FATAL ANY) | |
# remove the unusable binary | |
file(REMOVE "${_${RESULT_VARIABLE}}") | |
# find it by name, use it | |
find_program(_${RESULT_VARIABLE}_REAL "${_name}" | |
HINTS "${_working_dir}/.${_name}" "${_working_dir}/.${_name}/bin" "${_working_dir}/.${_name}/usr/bin" | |
REQUIRED) | |
if("${_name}" STREQUAL "makeself.sh") | |
# skip symlink, it will prevent makeself.sh from finding makeself-header.sh | |
set(_${RESULT_VARIABLE} "${_${RESULT_VARIABLE}_REAL}") | |
else() | |
# symlink the expected binary name | |
create_symlink("${_${RESULT_VARIABLE}_REAL}" "${_${RESULT_VARIABLE}}") | |
endif() | |
endif() | |
# Test the binary | |
# - TODO: Add support for bad binaries that set "$?" to an error code for no good reason | |
# - TODO: Add support for Windows binaries expecting "/?" instead of "--help" | |
message(STATUS "Testing that ${_name} works on this system...") | |
if(_${RESULT_VARIABLE}_USES_MAKESELF) | |
set(_test_param "--version") | |
else() | |
set(_test_param "--help") | |
endif() | |
execute_process(COMMAND "${_${RESULT_VARIABLE}}" ${_test_param} | |
COMMAND_ECHO ${_command_echo} | |
${_output_quiet} | |
${_error_quiet} | |
COMMAND_ERROR_IS_FATAL ANY) | |
message(STATUS "The binary \"${_${RESULT_VARIABLE}}\" is now available") | |
set(${RESULT_VARIABLE} "${_${RESULT_VARIABLE}}") | |
endmacro() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment