Changeset View
Changeset View
Standalone View
Standalone View
cmake/modules/NativeExecutable.cmake
# Allow to easily build native executable. | # Allow to easily build native executable. | ||||
# Useful for cross compilation. | # Useful for cross compilation. | ||||
if(NOT DEFINED __IS_NATIVE_BUILD) | |||||
# Check if we are in a native build or not. | |||||
set(__IS_NATIVE_BUILD 0 CACHE INTERNAL "Indicate if this is a native build") | |||||
endif() | |||||
if(__IS_NATIVE_BUILD AND CMAKE_CROSSCOMPILING) | |||||
message(FATAL_ERROR "A native build cannot be cross compiled") | |||||
endif() | |||||
# It is imperative that NATIVE_BUILD_DIR be in the cache. | |||||
set(NATIVE_BUILD_DIR "${CMAKE_BINARY_DIR}/native" CACHE PATH "The path of the native build directory" FORCE) | |||||
# Only ninja support depfiles and this is a hard error with other generators | # Only ninja support depfiles and this is a hard error with other generators | ||||
# so we need a nice wrapper to handle this mess. | # so we need a nice wrapper to handle this mess. | ||||
include(CustomCommandWithDepFile) | include(CustomCommandWithDepFile) | ||||
function(absolute_paths ABSOLUTE_PATHS) | |||||
foreach(RELATIVE_PATH ${ARGN}) | |||||
get_filename_component(ABSOLUTE_PATH "${RELATIVE_PATH}" ABSOLUTE) | |||||
list(APPEND ABSOLUTE_PATH_LIST "${ABSOLUTE_PATH}") | |||||
endforeach() | |||||
list(JOIN ABSOLUTE_PATH_LIST " " ${ABSOLUTE_PATHS}) | |||||
set(${ABSOLUTE_PATHS} "${${ABSOLUTE_PATHS}}" PARENT_SCOPE) | |||||
endfunction() | |||||
function(add_native_executable NAME) | function(add_native_executable NAME) | ||||
if(__IS_NATIVE_BUILD) | cmake_parse_arguments( | ||||
add_executable(${NAME} EXCLUDE_FROM_ALL ${ARGN}) | "" | ||||
# Multi-configuration generators (VS, Xcode) append a per-configuration | "" | ||||
# subdirectory to the specified directory unless the | "" | ||||
# `RUNTIME_OUTPUT_DIRECTORY` property is defined using a generator | "INCLUDE_DIRS;SOURCES" | ||||
# expression. | ${ARGN} | ||||
# Since we don't care about the build configuration for native | ) | ||||
# executables, we can simply drop this subdirectory. | |||||
# Doing so ensure that the path to the binary can always be retrieved. | # Inherits from the caller directory include directories | ||||
set_target_properties(${NAME} PROPERTIES | get_property(DIRECTORY_INCLUDE_DIRECTORIES DIRECTORY PROPERTY INCLUDE_DIRECTORIES) | ||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$<0:>" | |||||
) | # Since the CMakeLists file will be generated outside of the source tree, we | ||||
else() | # need to use absolute paths. | ||||
set(NATIVE_TARGET "${NAME}") | absolute_paths(ABS_SOURCES ${_SOURCES}) | ||||
file(RELATIVE_PATH RELATIVE_PATH "${CMAKE_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}") | absolute_paths(ABS_INCLUDE_DIRS ${DIRECTORY_INCLUDE_DIRECTORIES} ${_INCLUDE_DIRS}) | ||||
if(RELATIVE_PATH) | |||||
string(PREPEND NATIVE_TARGET "${RELATIVE_PATH}/") | set(NATIVE_LIST_DIR "${CMAKE_BINARY_DIR}/native/${NAME}") | ||||
endif() | set(NATIVE_BUILD_DIR "${NATIVE_LIST_DIR}/build") | ||||
if("${CMAKE_GENERATOR}" MATCHES "Ninja") | |||||
set(TARGET "${NATIVE_TARGET}") | # Needed for the depfile generation | ||||
else() | file(RELATIVE_PATH RELATIVE_BINARY_DIR "${CMAKE_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}") | ||||
set(TARGET "${NAME}") | file(RELATIVE_PATH RELATIVE_NATIVE_BUILD_DIR "${CMAKE_BINARY_DIR}" "${NATIVE_BUILD_DIR}") | ||||
endif() | |||||
configure_file( | |||||
"${CMAKE_SOURCE_DIR}/cmake/templates/NativeCMakeLists.txt.in" | |||||
"${NATIVE_LIST_DIR}/CMakeLists.txt" | |||||
) | |||||
set(NATIVE_BINARY "${NATIVE_BUILD_DIR}/${NATIVE_TARGET}") | file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/config") | ||||
configure_file( | |||||
"${CMAKE_SOURCE_DIR}/cmake/templates/NativeCmakeRunner.cmake.in" | |||||
"${CMAKE_BINARY_DIR}/config/run_native_cmake_${NAME}.sh" | |||||
) | |||||
set(NATIVE_BINARY "${NATIVE_BUILD_DIR}/${NAME}") | |||||
set(NATIVE_LINK "${CMAKE_CURRENT_BINARY_DIR}/native-${NAME}") | set(NATIVE_LINK "${CMAKE_CURRENT_BINARY_DIR}/native-${NAME}") | ||||
configure_file( | configure_file( | ||||
"${CMAKE_SOURCE_DIR}/cmake/templates/NativeBuildRunner.cmake.in" | "${CMAKE_SOURCE_DIR}/cmake/templates/NativeBuildRunner.cmake.in" | ||||
"${CMAKE_CURRENT_BINARY_DIR}/build_native_${NAME}.sh" | "${CMAKE_CURRENT_BINARY_DIR}/build_native_${NAME}.sh" | ||||
) | ) | ||||
add_custom_command_with_depfile( | |||||
OUTPUT "${NATIVE_BUILD_DIR}/CMakeCache.txt" | |||||
COMMENT "Preparing native build for ${NAME}..." | |||||
COMMAND "${CMAKE_BINARY_DIR}/config/run_native_cmake_${NAME}.sh" | |||||
DEPENDS "${CMAKE_BINARY_DIR}/config/run_native_cmake_${NAME}.sh" | |||||
DEPFILE "${NATIVE_BUILD_DIR}/CMakeFiles/CMakeCache.txt.d" | |||||
VERBATIM USES_TERMINAL | |||||
) | |||||
add_custom_target(native-cmake-build-${NAME} | |||||
DEPENDS "${NATIVE_BUILD_DIR}/CMakeCache.txt" | |||||
) | |||||
# We create a symlink because cmake craps itself if the imported | # We create a symlink because cmake craps itself if the imported | ||||
# executable has the same name as the executable itself. | # executable has the same name as the executable itself. | ||||
# https://cmake.org/pipermail/cmake/2019-May/069480.html | # https://cmake.org/pipermail/cmake/2019-May/069480.html | ||||
add_custom_command_with_depfile( | add_custom_command_with_depfile( | ||||
OUTPUT "${NATIVE_LINK}" | OUTPUT "${NATIVE_LINK}" | ||||
COMMENT "Building native ${NATIVE_TARGET}" | COMMENT "Building native ${NAME}" | ||||
COMMAND "${CMAKE_CURRENT_BINARY_DIR}/build_native_${NAME}.sh" | COMMAND "${CMAKE_CURRENT_BINARY_DIR}/build_native_${NAME}.sh" | ||||
DEPENDS | DEPENDS | ||||
native-cmake-build | native-cmake-build-${NAME} | ||||
"${CMAKE_CURRENT_BINARY_DIR}/build_native_${NAME}.sh" | "${CMAKE_CURRENT_BINARY_DIR}/build_native_${NAME}.sh" | ||||
${ARGN} | ${ABS_SOURCES} | ||||
DEPFILE "${NATIVE_LINK}.d" | DEPFILE "${NATIVE_LINK}.d" | ||||
VERBATIM USES_TERMINAL | VERBATIM USES_TERMINAL | ||||
) | ) | ||||
add_executable(${NAME} IMPORTED GLOBAL) | add_executable(${NAME} IMPORTED GLOBAL) | ||||
set_target_properties(${NAME} PROPERTIES IMPORTED_LOCATION "${NATIVE_BINARY}") | set_target_properties(${NAME} PROPERTIES IMPORTED_LOCATION "${NATIVE_BINARY}") | ||||
# This obviously cannot depend on a file for some mysterious reasons only | # This obviously cannot depend on a file for some mysterious reasons only | ||||
# the cmake gods are aware of, so we need a phony custom target. | # the cmake gods are aware of, so we need a phony custom target. | ||||
add_custom_target("build-native-${NAME}" DEPENDS "${NATIVE_LINK}") | add_custom_target("build-native-${NAME}" DEPENDS "${NATIVE_LINK}") | ||||
add_dependencies(${NAME} "build-native-${NAME}") | add_dependencies(${NAME} "build-native-${NAME}") | ||||
endif() | |||||
endfunction(add_native_executable) | |||||
function(native_target_include_directories) | |||||
if(__IS_NATIVE_BUILD) | |||||
target_include_directories(${ARGN}) | |||||
endif() | |||||
endfunction(native_target_include_directories) | |||||
function(native_add_cmake_flags) | |||||
set_property(GLOBAL APPEND PROPERTY _NATIVE_BUILD_CMAKE_FLAGS ${ARGN}) | |||||
endfunction(native_add_cmake_flags) | |||||
# Internal machinery | |||||
function(_gen_native_cmake_target) | |||||
message(STATUS "Configuring native build in ${NATIVE_BUILD_DIR}") | |||||
get_property(ARGSLIST GLOBAL PROPERTY _NATIVE_BUILD_CMAKE_FLAGS) | |||||
list(SORT ARGSLIST) | |||||
list(REMOVE_DUPLICATES ARGSLIST) | |||||
list(JOIN ARGSLIST " " ARGS) | |||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/config") | |||||
configure_file( | |||||
"${CMAKE_SOURCE_DIR}/cmake/templates/NativeCmakeRunner.cmake.in" | |||||
"${CMAKE_BINARY_DIR}/config/run_native_cmake.sh" | |||||
) | |||||
endfunction(_gen_native_cmake_target) | |||||
function(_gen_native_cmake_hook VAR ACCESS) | |||||
# When CMAKE_CURRENT_LIST_DIR is set to empty, we execute everything. | |||||
if("${VAR}" STREQUAL "CMAKE_CURRENT_LIST_DIR" AND | |||||
"${CMAKE_CURRENT_LIST_DIR}" STREQUAL "" AND | |||||
"${ACCESS}" STREQUAL "MODIFIED_ACCESS") | |||||
_gen_native_cmake_target() | |||||
endif() | |||||
endfunction(_gen_native_cmake_hook) | |||||
if(NOT __IS_NATIVE_BUILD AND NOT TARGET native-cmake-build) | |||||
# Set a hook to execute when everything is set. | |||||
variable_watch(CMAKE_CURRENT_LIST_DIR _gen_native_cmake_hook) | |||||
add_custom_command_with_depfile( | # Cleanup the native build directory. | ||||
OUTPUT "${NATIVE_BUILD_DIR}/CMakeCache.txt" | set_property( | ||||
COMMENT "Preparing native build..." | DIRECTORY "${CMAKE_SOURCE_DIR}" | ||||
COMMAND "${CMAKE_BINARY_DIR}/config/run_native_cmake.sh" | APPEND PROPERTY ADDITIONAL_CLEAN_FILES | ||||
DEPENDS "${CMAKE_BINARY_DIR}/config/run_native_cmake.sh" | "${NATIVE_BUILD_DIR}" | ||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" | |||||
DEPFILE "${NATIVE_BUILD_DIR}/CMakeFiles/CMakeCache.txt.d" | |||||
VERBATIM USES_TERMINAL | |||||
) | ) | ||||
endfunction() | |||||
add_custom_target(native-cmake-build DEPENDS "${NATIVE_BUILD_DIR}/CMakeCache.txt") | |||||
# Add the native directory to the list of file to cleanup. | |||||
set_property(DIRECTORY "${CMAKE_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_CLEAN_FILES "${NATIVE_BUILD_DIR}") | |||||
endif() |