cmake/exeutils.cmake (208 lines of code) (raw):

# Copyright (c) 2019, 2025, Oracle and/or its affiliates. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2.0, # as published by the Free Software Foundation. # # This program is designed to work with certain software (including # but not limited to OpenSSL) that is licensed under separate terms, # as designated in a particular file or component or in included license # documentation. The authors of MySQL hereby grant you an additional # permission to link the program and your derivative works with the # separately licensed software that they have either included with # the program or referenced in the documentation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See # the GNU General Public License, version 2.0, for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA function(add_shell_executable) # ARGV0 - name # ARGV1 - sources # ARGV2 - (optional) if true skips install message(STATUS "Adding executable ${ARGV0}") add_executable("${ARGV0}" ${ARGV1}) set_target_properties("${ARGV0}" PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${INSTALL_BINDIR}") fix_target_output_directory("${ARGV0}" "${INSTALL_BINDIR}") if(BUNDLED_OPENSSL AND NOT WIN32) target_link_libraries("${ARGV0}" "-L${OPENSSL_DIRECTORY}" ) if(NOT APPLE) target_link_libraries("${ARGV0}" "-Wl,-rpath-link,${OPENSSL_DIRECTORY}" ) endif() if(NOT CRYPTO_DIRECTORY STREQUAL OPENSSL_DIRECTORY) target_link_libraries("${ARGV0}" "-L${CRYPTO_DIRECTORY}" ) if(NOT APPLE) target_link_libraries("${ARGV0}" "-Wl,-rpath-link,${CRYPTO_DIRECTORY}" ) endif() endif() endif() if(NOT ARGV2) message(STATUS "Marking executable ${ARGV0} as a run-time component") install(TARGETS "${ARGV0}" RUNTIME COMPONENT main DESTINATION "${INSTALL_BINDIR}") endif() if(APPLE) if(BUNDLED_OPENSSL) add_custom_command(TARGET "${ARGV0}" POST_BUILD COMMAND install_name_tool -change "${CRYPTO_VERSION}" "@loader_path/../${INSTALL_LIBDIR}/${CRYPTO_VERSION}" $<TARGET_FILE:${ARGV0}> COMMAND install_name_tool -change "${OPENSSL_VERSION}" "@loader_path/../${INSTALL_LIBDIR}/${OPENSSL_VERSION}" $<TARGET_FILE:${ARGV0}> ) endif() if(BUNDLED_SHARED_PYTHON) get_filename_component(PYTHON_VERSION "${PYTHON_LIBRARIES}" NAME) add_custom_command(TARGET "${ARGV0}" POST_BUILD COMMAND install_name_tool -change "${PYTHON_VERSION}" "@loader_path/../${INSTALL_LIBDIR}/${PYTHON_VERSION}" $<TARGET_FILE:${ARGV0}> ) endif() if(BUNDLED_SSH_DIR) get_target_property(_SSH_LIBRARY_LOCATION ssh LOCATION) get_filename_component(_SSH_LIBRARY_NAME ${_SSH_LIBRARY_LOCATION} NAME) add_custom_command(TARGET "${ARGV0}" POST_BUILD COMMAND install_name_tool -change "${_SSH_LIBRARY_NAME}" "@loader_path/../${INSTALL_LIBDIR}/${_SSH_LIBRARY_NAME}" $<TARGET_FILE:${ARGV0}>) endif() if(BUNDLED_ANTLR_DIR) add_custom_command(TARGET "${ARGV0}" POST_BUILD COMMAND install_name_tool -change "${ANTLR4_LIB_FILENAME}" "@loader_path/../${INSTALL_LIBDIR}/${ANTLR4_LIB_FILENAME}" $<TARGET_FILE:${ARGV0}>) endif() if(JIT_EXECUTOR_LIB) add_custom_command(TARGET "${ARGV0}" POST_BUILD COMMAND install_name_tool -change "${JIT_EXECUTOR_LIBRARY_NAME}" "@loader_path/../${INSTALL_LIBDIR}/${JIT_EXECUTOR_LIBRARY_NAME}" $<TARGET_FILE:${ARGV0}> ) endif() elseif(NOT WIN32) if(BUNDLED_OPENSSL OR BUNDLED_SHARED_PYTHON OR BUNDLED_SSH_DIR OR BUNDLED_KRB5_DIR OR BUNDLED_ANTLR_DIR OR BUNDLED_SASL_DIR) # newer versions of linker enable new dtags by default, causing -Wl,-rpath to create RUNPATH # entry instead of RPATH this results in loader loading system libraries (if they are available) # instead of bundled ones, this has special impact when bundled libraries are linked using a bundled # version of OpenSSL (which might be newer than the system one) set_property(TARGET "${ARGV0}" PROPERTY LINK_OPTIONS "-Wl,--disable-new-dtags") set_property(TARGET "${ARGV0}" PROPERTY INSTALL_RPATH "\$ORIGIN/../${INSTALL_LIBDIR}") set_property(TARGET "${ARGV0}" PROPERTY PROPERTY BUILD_WITH_INSTALL_RPATH TRUE) endif() endif() if(HAVE_JS) # strip the whole binary on 32bit Ubuntu 18.04, avoid OOM linker errors if(LINUX_UBUNTU_18_04 AND CMAKE_SIZEOF_VOID_P EQUAL 4) MY_TARGET_LINK_OPTIONS("${ARGV0}" "LINKER:--strip-all") endif() endif() endfunction() function(get_rpath_command) set(options) set(oneValueArgs BINARY DESTINATION OUT_COMMAND) set(multiValueArgs) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) file(RELATIVE_PATH RELATIVE_PATH_TO_LIBDIR "${CONFIG_BINARY_DIR}/${ARG_DESTINATION}" "${CONFIG_BINARY_DIR}/${INSTALL_LIBDIR}") # if RELATIVE_PATH_TO_LIBDIR is not set, then binaries are copied to the INSTALL_LIBDIR, in which case rpath should be $ORIGIN # in all other cases we want to separate $ORIGIN with a slash if(RELATIVE_PATH_TO_LIBDIR) string(PREPEND RELATIVE_PATH_TO_LIBDIR "/") endif() set("${ARG_OUT_COMMAND}" ${PATCHELF_EXECUTABLE} ${PATCHELF_PAGE_SIZE_ARGS} --set-rpath "\\$$ORIGIN${RELATIVE_PATH_TO_LIBDIR}" "${ARG_BINARY}" PARENT_SCOPE) endfunction() function(install_bundled_binaries) set(options) set(oneValueArgs DESTINATION TARGET WRITE_RPATH) set(multiValueArgs BINARIES) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT DEFINED ARG_WRITE_RPATH) # if not explicitly specified, we always add an RPATH set(ARG_WRITE_RPATH TRUE) endif() if(WIN32 AND (ARG_DESTINATION STREQUAL INSTALL_LIBDIR OR ARG_DESTINATION MATCHES "^${INSTALL_LIBDIR}/.*")) # on Windows, if files should be installed in the 'lib' directory, we install them in the 'bin' directory instead set(ARG_DESTINATION "${INSTALL_BINDIR}") endif() SET(NORMALIZED_BINARIES "") foreach(SOURCE_BINARY ${ARG_BINARIES}) FILE(TO_CMAKE_PATH "${SOURCE_BINARY}" SOURCE_BINARY) list(APPEND NORMALIZED_BINARIES "${SOURCE_BINARY}") endforeach() SET(ARG_BINARIES ${NORMALIZED_BINARIES}) SET(COPIED_BINARIES "") set(DESTINATION_BINARY_DIR "${CONFIG_BINARY_DIR}/${ARG_DESTINATION}") if(WIN32 AND CMAKE_VERSION VERSION_LESS "3.20.0") # on Windows, CONFIG_BINARY_DIR is using a generator expression, which is supported by OUTPUT of add_custom_command starting with 3.20 set(DESTINATION_BINARY_DIR "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${ARG_DESTINATION}") endif() add_custom_command(TARGET ${ARG_TARGET} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "${DESTINATION_BINARY_DIR}") foreach(SOURCE_BINARY ${ARG_BINARIES}) get_filename_component(SOURCE_BINARY_NAME "${SOURCE_BINARY}" NAME) set(COPIED_BINARY "${DESTINATION_BINARY_DIR}/${SOURCE_BINARY_NAME}") set(COPY_TARGET "copy_${SOURCE_BINARY_NAME}") SET(RPATH_COMMAND "") if(NOT IS_SYMLINK "${SOURCE_BINARY}") message(STATUS "Bundling binary: ${SOURCE_BINARY}, installation directory: ${ARG_DESTINATION}") if(LINUX) if(ARG_WRITE_RPATH) get_rpath_command(BINARY "${COPIED_BINARY}" DESTINATION "${ARG_DESTINATION}" OUT_COMMAND RPATH_COMMAND) message(STATUS " will execute: ${RPATH_COMMAND}") endif() elseif(APPLE) # if we bundle OpenSSL, then we want the bundled binary to use it instead of the system one if(BUNDLED_OPENSSL) SET_BUNDLED_OPEN_SSL(BINARIES "${SOURCE_BINARY}") endif() endif() endif() # create a dependency, so that files are copied only if source changes add_custom_command(OUTPUT "${COPIED_BINARY}" COMMAND ${CMAKE_COMMAND} -Dsrc_file="${SOURCE_BINARY}" -Ddst_dir="${DESTINATION_BINARY_DIR}" -P "${CMAKE_SOURCE_DIR}/cmake/copy_file.cmake" COMMAND ${RPATH_COMMAND} DEPENDS "${SOURCE_BINARY}" ) add_custom_target("${COPY_TARGET}" DEPENDS "${COPIED_BINARY}" ) add_dependencies("${ARG_TARGET}" "${COPY_TARGET}") list(APPEND COPIED_BINARIES "${COPIED_BINARY}") endforeach() # we're installing copied binaries, so that source files remain intact (i.e. RPATH is written to the copy, not the original) INSTALL(PROGRAMS ${COPIED_BINARIES} DESTINATION "${ARG_DESTINATION}" COMPONENT main) endfunction() function(generate_rc_file) set(options) set(oneValueArgs NAME DESCRIPTION OUTPUT_DIR OUT_RC_FILE) set(multiValueArgs) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(MYSH_ORIGINAL_FILE_NAME "${ARG_NAME}") get_filename_component(MYSH_INTERNAL_NAME "${ARG_NAME}" NAME_WLE) set(MYSH_FILE_DESCRIPTION "${ARG_DESCRIPTION}") get_filename_component(_ext "${ARG_NAME}" LAST_EXT) if(_ext STREQUAL ".exe") set(MYSH_FILE_TYPE "VFT_APP") elseif(_ext STREQUAL ".dll") set(MYSH_FILE_TYPE "VFT_DLL") else() message(FATAL_ERROR "Unsupported file type: ${_ext}") endif() if(NOT ARG_OUTPUT_DIR) set(ARG_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") endif() set(_rc_file "${ARG_OUTPUT_DIR}/${MYSH_INTERNAL_NAME}.rc") configure_file("${PROJECT_SOURCE_DIR}/res/resource.rc.in" "${_rc_file}" @ONLY) set(${ARG_OUT_RC_FILE} "${_rc_file}" PARENT_SCOPE) endfunction()