How to combine multiple imported targets into one - cmake

Having been unable to find an answer to this question, I decided to post it here.
I'm trying to setup a small cmake-based build and am not sure how should I tackle it.
I realize the question addresses a "how" and not a "what" but I am open for any alternative solution that doesn't feel like a hack.
I need to add a post build command to copy some (imported) dlls to my target's output:
add_custom_command(TARGET MyTarget POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:MyImportedDlls::All>
$<TARGET_FILE_DIR:MyTarget>
)
I made a "find" script for the imported dlls that creates one target for each dll
set(LIB_FILES
"${PATH_TO_LIBS}/lib1.dll"
"${PATH_TO_LIBS}/lib2.dll"
"${PATH_TO_LIBS}/lib3.dll"
)
unset(TARGETS)
foreach(LIB_FILE ${LIB_FILES})
get_filename_component(FN ${LIB_FILE} NAME_WE)
add_library(MyImportedDlls::${FN} SHARED IMPORTED)
set_target_properties(MyImportedDlls::${FN} PROPERTIES IMPORTED_LOCATION "${LIB_FILE}")
list(APPEND TARGETS "MyImportedDlls::${FN}")
endforeach()
My problem is: how to create a "combined" target that I can use in my custom command?

Just have 3 post build commands, one for each target.
foreach(target IN LISTS TARGETS)
add_custom_command(TARGET MyTarget POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:${target}>
$<TARGET_FILE_DIR:MyTarget>
)
endforeach()

Related

What is the function of add_custom_target in CMake?

From the CMake Cookbook, I see that we can use the command add_custom_command and add_custom_target to run a custom command at build time. There is a toy example that I want to extract compressed files in subdirectory and link it to the final executable files. We have two CMakeLists.txt files and the following one is in the subdirectory.
find_package(BLAS REQUIRED)
find_package(LAPACK REQUIRED)
set(MATH_SRCS
${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxBLAS.cpp
${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxLAPACK.cpp
${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxBLAS.hpp
${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK/CxxLAPACK.hpp
)
add_custom_target(BLAS_LAPACK_wrappers
WORKING_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}
DEPENDS
${MATH_SRCS}
COMMENT
"Intermediate BLAS_LAPACK_wrappers target"
VERBATIM
)
add_custom_command(
OUTPUT
${MATH_SRCS}
COMMAND
${CMAKE_COMMAND} -E tar xzf ${CMAKE_CURRENT_SOURCE_DIR}/wrap_BLAS_LAPACK.tar.gz
WORKING_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/wrap_BLAS_LAPACK.tar.gz
COMMENT
"Unpacking C++ wrappers for BLAS/LAPACK"
)
add_library(math "")
target_sources(math
PRIVATE
${MATH_SRCS}
)
target_include_directories(math
INTERFACE
${CMAKE_CURRENT_BINARY_DIR}/wrap_BLAS_LAPACK
)
# BLAS_LIBRARIES are included in LAPACK_LIBRARIES
target_link_libraries(math
PUBLIC
${LAPACK_LIBRARIES}
)
The following CMakeLists.txt is in the main directory.
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
# Fortran needed to discover LAPACK with some compilers
project(recipe-04 LANGUAGES CXX Fortran)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(deps)
add_executable(linear-algebra linear-algebra.cpp)
target_link_libraries(linear-algebra
PRIVATE
math
)
I do not know why we need the add_custom_target in this example.
The command add_custom_command cannot be executed without generating any target. That is why we need the custom target to run this command. What's more, it can only execute at the configure time. If we want to run the command after we configure the CMakeLists.txt file, we can create a custom target to achieve that as the following.
add_custom_target(BLAS_LAPACK_wrappers
WORKING_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}
DEPENDS
${MATH_SRCS}
COMMENT
"Intermediate BLAS_LAPACK_wrappers target"
VERBATIM
)
add_custom_command(
OUTPUT
${MATH_SRCS}
COMMAND
${CMAKE_COMMAND} -E tar xzf ${CMAKE_CURRENT_SOURCE_DIR}/wrap_BLAS_LAPACK.tar.gz
WORKING_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/wrap_BLAS_LAPACK.tar.gz
COMMENT
"Unpacking C++ wrappers for BLAS/LAPACK"
)
After we configure the CMakeLists.txt file, we use the Cmake --build . --target=BLAS_LAPACK_wrappers the execute it at the build time.

CMake custom target doesn't build

I'm building two targets using:
add_library(tgt1 SHARED a.cpp)
add_library(tgt2 SHARED b.cpp)
After both are built, I need to run a post build step that depends on both targets. I tried many combinations of the following but with no success:
add_custom_target(final_tgt DEPENDS tgt1 tgt2)
add_custom_command(TARGET final_tgt POST_BUILD COMMAND <command> ARGS <args>)
The final target would simply not build, even though its build.make contains the custom command.
Tried to use ALL for the custom target, however make attempts to build it first while missing the first targets.
And I can't use an add_library or add_executable for the final target, since they require specifying source files.
What is the correct way to do it?
===================================
Edit: below is a minimal verifiable source code.
What it attempts to do is to compile code (for Mac) in two architectures and as a post-build to create a universal binary using lipo:
cmake_minimum_required(VERSION 2.8)
set(icpc_req_path "/usr/local/bin/icpc-16.0.146")
set(CMAKE_CXX_COMPILER "${icpc_req_path}")
project("CMakeTest")
set(SOURCE_FILES a.cpp)
set (TARGET_NAME "TGT")
set(TARGETS "")
set(ARCHITECTURES i386 x86_64)
foreach(ar ${ARCHITECTURES})
set(CMAKE_CXX_FLAGS_RELEASE "")
set(CMAKE_CXX_FLAGS_DEBUG "")
set(CMAKE_CXX_FLAGS "")
add_library(TGT_${ar} SHARED ${SOURCE_FILES})
set_target_properties(${TARGET_NAME}_${ar}
PROPERTIES COMPILE_FLAGS "-arch ${ar} -xSSE3")
set_target_properties(${TARGET_NAME}_${ar}
PROPERTIES LINK_FLAGS "-arch ${ar}")
set(TARGETS "${TARGETS};lib${TARGET_NAME}_${ar}.dylib")
endforeach(ar)
message("Targets: ${TARGETS}")
add_custom_target(${TARGET_NAME} DEPENDS ${TARGETS})
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND "lipo"
ARGS "-create" ${TARGETS} "-output" "${TARGET_NAME}.dylib")
And the contents of a.cpp is:
int main(){}
add_custom_command comes in two flavors: Producing new output, and acting on single targets. Docs
What is the command that you are calling doing? Is it in any way producing results (new files, etc)? If so, use add_custom_command like this:
add_custom_command(
OUTPUT <output-file>
DEPENDS tgt1 tgt2
COMMAND <command>
ARGS <args>
COMMENT "Running <command> on targets tgt1 and tgt2."
)
Using the second variant of add_custom_command which does not have an OUTPUT argument, because it changes a <target> as POST_BUILD (or pre-build, pre-link) step, needs a single target. So, which one of tgt1 and tgt2 gets modified by your <command>?
Let's assume that tgt1 gets modified in the POST_BUILD step and tgt2 is untouched. Then you can do it like this:
add_library(tgt2 SHARED b.cpp)
add_library(tgt1 SHARED a.cpp)
add_custom_command(
TARGET tgt1 POST_BUILD
COMMAND <command>
ARGS <args>
)
add_dependencies(tgt1 tgt2) # tgt1 depends on tgt2 because
# POST_BUILD-step is just the final step to build 'tgt1'
# NOTE: It is incorrect to modify 'tgt2' as POST_BUILD step for tgt1.
# So this example expects no changes to tgt2 in add_custom_command.
--
EDIT after more details given in question:
Working example
CMakeLists.txt
# I don't have 'icpc' and could not find it easily available for macOS.
# Instead, let's create a file "TGT" where contents are the two hashes of the two
# libraries, like done in 'Th.Thielemann's answer.
cmake_minimum_required(VERSION 3.10)
project(q50198141)
add_library(Big SHARED library1.cpp)
add_library(Foo SHARED library2.cpp)
add_custom_command(OUTPUT combined
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/combine.sh
ARGS $<TARGET_FILE:Big> $<TARGET_FILE:Foo> combined
DEPENDS Big Foo combine.sh
COMMENT Build output 'combined'
)
add_custom_target(run_combined ALL DEPENDS combined)
combined.sh (Make sure to be excecutable!)
#!/bin/bash
# Hardcoded for q50198141
# Args: In1 In2 Out
md5sum $1 $2 > $3
The following example works for me.
Two targets independent from each other. One custom target depending from both.
add_library(Big SHARED ${SOURCES_BIG})
add_library(Foo SHARED ${SOURCES_FOO})
add_custom_target(pack ALL
COMMAND ${CMAKE_COMMAND} -E md5sum ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}Big${CMAKE_SHARED_LIBRARY_SUFFIX}
COMMAND ${CMAKE_COMMAND} -E md5sum ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}Foo${CMAKE_SHARED_LIBRARY_SUFFIX}
DEPENDS Big Foo
COMMENT Build target pack
)
Note: Calculating the hash of the code is just to show whether targets are rebuild on changes or not.

Random executable output with CMake

Can I have a random name for the executable file of each build?
Or, in another words, a different name for the executable of each build action?
I wonder if a random-variable could be inserted into the build-tool-chain.
The reason of such a name is that my company's virus-checking is quite slow -- it took a long long time checking each executable, even longer then the build.
I'm using CLion 2016.2 on Win7, tool-chain is MinGW_w64_5.0, bundled CMake 3.5.2
You could always define POST_BUILD steps that call another CMake script. The only downside in the following approach would be that you can't - since it's random - reuse the executable's output name in CMake itself:
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(RandomExeName)
file(WRITE main.cpp "int main() { return 0; }")
add_executable(${PROJECT_NAME} main.cpp)
add_custom_command(
TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -D _file:PATH="$<TARGET_FILE:${PROJECT_NAME}>"
-P ${CMAKE_SOURCE_DIR}/CopyToRandom.cmake
)
set_property(TARGET ${PROJECT_NAME} PROPERTY SUFFIX ".temp")
CopyToRandom.cmake
string(RANDOM _random)
file(GLOB _old_files RELATIVE "${CMAKE_BINARY_DIR}" "*.exe")
execute_process(
COMMAND "${CMAKE_COMMAND}" -E remove ${_old_files}
COMMAND "${CMAKE_COMMAND}" -E copy "${_file}" "${_random}.exe"
)
# generate shortcut
get_filename_component(_name "${_file}" NAME_WE)
file(
WRITE "${_name}.sh"
"#!/bin/bash\n"
"${_random}.exe"
)
No you can't. Or you have to reconfigure for every build.
Regarding your actual problem: Advice the virus checker to exclude your build directories.

Error with variables when invoking CMake from CMake

I need to invoke cmake from within cmake so that I can have binaries built before project files are generated. I have the following CmakeLists.txt:
cmake_minimum_required(VERSION 3.2)
project(StarEngine)
set(CMAKE_CXX_FLAGS "-std=c++11")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
find_package(OpenGL REQUIRED)
include_directories(${OPENGL_INCLUDE_DIR})
#copy the other cmake file into where we'd like to invoke cmake
configure_file(deps-CMakeLists.txt deps/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} . WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/deps)
#eventually binaries will be built, for now this doesn't accomplish anything
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/deps)
And its friend, deps-CmakeLists.txt, a test script:
set(GENERATED_DIR "test")
MESSAGE( STATUS ${GENERATED_DIR} )
In the following file structure:
Project
build
Code
CmakeLists.txt
deps-CmakeLists.txt
No matter what I put in for the variable value, it is blank when displayed in MESSAGE. I imagine this is weird behavior resulting from invoking cmake from cmake. I had a bunch of other strange errors to, but I suspect if I can figure out this one that will help crack them all.
Thanks to #Florian, the problem was with variable replacement and I needed to add the COPYONLY option to configure_file

Using cmake with a custom file generator

I'd like to use CMake to generate obfuscated lua files for delivery. For the life of me I cannot get add_custom_command + add_custom_target to build these files for me. There's something I'm missing.
ADD_CUSTOM_TARGET(LUABIND_COMPILED_FILES ALL)
FOREACH(F ${LUA_SCRIPT_FILES})
ADD_CUSTOM_COMMAND(
OUTPUT ${LUA_COMPILED_SCRIPTS}/${F}
COMMAND ${LUAC} -o ${LUA_COMPILED_SCRIPTS}/${F}
COMMENT "Compiling ${F} to binary"
ADD_DEPENDENCIES(LUABIND_COMPILED_FILES ${LUA_COMPILED_SCRIPTS}/${F})
ENDFOREACH()
For some reason when I run cmake + make the output tells me there's nothing to be done for target LUABIND_COMPILED_FILES. Am I missing something here? Thanks in advance.
The ADD_DEPENDENCIES command can only be used to add dependencies between top-level targets. The ADD_CUSTOM_COMMAND command however generates output files, but does not add new targets.
To make a custom target depend on generated files, use the DEPENDS options of the add_custom_target command:
set (LUA_COMPILED_FILES "")
foreach(F ${LUA_SCRIPT_FILES})
add_custom_command(
OUTPUT "${LUA_COMPILED_SCRIPTS}/${F}"
COMMAND ${LUAC} -o "${LUA_COMPILED_SCRIPTS}/${F}"
COMMENT "Compiling ${F} to binary")
list (APPEND LUA_COMPILED_FILES "${LUA_COMPILED_SCRIPTS}/${F}")
endforeach()
add_custom_target(LUABIND ALL DEPENDS ${LUA_COMPILED_FILES})