Cannot import a project twice when cross compiling (as host-tool and for the target arch)
Problem
The user cannot import a project twice to get the targets when cross-compiling: once for the host architecture and once for the target architecture.
Right now, the user needs to manually use e.g. find_program()
instead of relying on installed locations, which can be annoying if the binaries are installed outside the environment PATH
.
The problem is much more apparent in big superbuilds where you have hundreds of projects brought together in one build and no control over components outside your own.
Use Case
Consider the following use case:
- We are cross-compiling.
- A certain project A depends on project B in two different ways:
- it uses an executable target from B to generate some files, e.g. a compiler.
- one of its libraries links against a library of B.
We would like to be able to do:
find_package(B REQUIRED)
# use B::lib normally. When crosscompiling, it will be the one compiled for the target.
find_package(B REQUIRED NO_CMAKE_FIND_ROOT_PATH)
# use B::exe knowing that it will always run from the host system.
This is for instance a use case we have for flatbuffers / protobuf.
IMPORTED
and ALIAS
targets should not be global, or it should be possible to override them.
Or else, it should be possible to dynamically specify a namespace when importing targets through find_package().
Test case
Running the following example should give the version of sub::sub
for the host architecture (inside the host-tools/sub
folder) instead than the one for the target architecture.
# ------------- toplevel/CMakeLists.txt ------------
cmake_minimum_required(VERSION 3.6)
project(top)
add_subdirectory(sub)
add_executable(sub::sub ALIAS sub)
if(NOT CMAKE_CROSSCOMPILING)
message(FATAL_ERROR "
This is a test when cross-compiling. It does not
make sense otherwise. Try with e.g. the android
toolchain, by passing -DCMAKE_TOOLCHAIN_FILE=.
")
else()
set(workdir ${CMAKE_CURRENT_BINARY_DIR}/host-tools/sub)
file(MAKE_DIRECTORY ${workdir})
execute_process(COMMAND ${CMAKE_COMMAND} ${CMAKE_CURRENT_SOURCE_DIR}/sub
WORKING_DIRECTORY ${workdir})
execute_process(COMMAND ${CMAKE_COMMAND} --build .
WORKING_DIRECTORY ${workdir})
list(INSERT CMAKE_PREFIX_PATH 0 ${workdir})
find_package(sub REQUIRED NO_CMAKE_FIND_ROOT_PATH)
endif()
add_custom_target(print ALL
COMMAND ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:sub::sub>"
VERBATIM)
# ------------- toplevel/sub/CMakeLists.txt ------------
cmake_minimum_required(VERSION 3.6)
project(sub)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/main.cc "int main () { return 42; }")
add_executable(sub ${CMAKE_CURRENT_BINARY_DIR}/main.cc)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/sub-config.cmake "include(\${CMAKE_CURRENT_LIST_DIR}/sub-targets.cmake)")
install(TARGETS sub EXPORT sub DESTINATION bin/)
install(EXPORT sub DESTINATION lib/cmake NAMESPACE sub::)
export(EXPORT sub NAMESPACE sub:: FILE sub-targets.cmake)