Is it a bug of CMake, or may I have a mistake? Can anybody point out that how to fix the problem? Any help will be greatly appreciated.
First, I listed a third party target's dll files. I do believe that it is ok.
project(assimp)
add_library(${PROJECT_NAME} SHARED IMPORTED GLOBAL)
set(ASSIMP_ROOT ${CMAKE_CURRENT_LIST_DIR})
set(ASSIMP_NAME "assimp-vc142-mt")
set_target_properties(${PROJECT_NAME} PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${ASSIMP_ROOT}/include"
IMPORTED_IMPLIB_RELEASE "${ASSIMP_ROOT}/lib/${ASSIMP_NAME}.lib"
IMPORTED_IMPLIB_DEBUG "${ASSIMP_ROOT}/lib/${ASSIMP_NAME}d.lib"
IMPORTED_LOCATION_RELEASE "${ASSIMP_ROOT}/bin/${ASSIMP_NAME}.dll"
IMPORTED_LOCATION_DEBUG "${ASSIMP_ROOT}/bin/${ASSIMP_NAME}d.dll")
Then, I wanted to use this lib, and the problem came.
cmake_minimum_required(VERSION 3.15)
include(cmake/Functions.cmake)
project(my_show_case)
add_subdirectory(thirdparty)
add_executable(${PROJECT_NAME})
file(GLOB_RECURSE srcs ${CMAKE_CURRENT_LIST_DIR}/include/*.h ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp)
target_sources(${PROJECT_NAME} PUBLIC ${srcs})
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include/)
get_property(ASSIMP_DEBUG_LIB TARGET assimp PROPERTY IMPORTED_LOCATION_DEBUG)
get_property(ASSIMP_RELEASE_LIB TARGET assimp PROPERTY IMPORTED_LOCATION_RELEASE)
# This section would get a wrong file name which has a '>' suffix
set(MY_Lib $<$<CONFIG:Debug>:${ASSIMP_DEBUG_LIB}>$<$<CONFIG:Release>:${ASSIMP_RELEASE_LIB}>)
get_filename_component(FILENAME "${MY_Lib}" NAME)
# prints: "assimp-vc142-mt.dll>"
file(GENERATE OUTPUT TestOut.txt CONTENT "${FILENAME}")
The file TestOut.txt would print "assimp-vc142-mt.dll>", as you can see, it has a suffix ">".
Related
I get this error, which I've been trying to understand for some time now but getting nowhere.
add_library cannot create target "Plugin" because another target
with the same name already exists. The existing target is a shared library created
in source directory "D:/CHAI3D/SOFA/src/applications/plugins/plugin".
See documentation for policy CMP0002 for more details.
Below is the CMakelists.txt added for reference. I tried to remove the unnecessary code. So far, I've tried allowing duplicate targets with set(ALLOW_DUPLICATE_CUSTOM_TARGETS TRUE)
But to no avail
cmake_minimum_required(VERSION 3.1)
project(Plugin VERSION 21.06.99)
# Policies
cmake_policy(SET CMP0079 NEW)
set(ALLOW_DUPLICATE_CUSTOM_TARGETS TRUE)
set (PLUGIN_VERSION ${PROJECT_VERSION})
set(HEADER_FILES
src/initPlugin.h
...
)
set(SOURCE_FILES
src/initPlugin.cpp
...
)
file(GLOB_RECURSE RESOURCE_FILES "*.md" "*.psl" "*.py" "*.pyscn" "*.scn" "*.ah")
add_library(${PROJECT_NAME} SHARED ${HEADER_FILES} ${SOURCE_FILES} ${RESOURCE_FILES} )
target_include_directories(${PROJECT_NAME} PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>")
set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "-DSOFA_BUILD_PLUGIN")
target_link_libraries(${PROJECT_NAME} SofaCore SofaConstraint SofaSimpleFem SofaBaseMechanics SofaRigid SofaBaseVisual SofaOpenglVisual)
## Install rules for the library and headers; CMake package configurations files
sofa_create_package_with_targets(
PACKAGE_NAME ${PROJECT_NAME}
PACKAGE_VERSION ${PROJECT_VERSION}
TARGETS ${PROJECT_NAME} AUTO_SET_TARGET_PROPERTIES
INCLUDE_SOURCE_DIR "src"
INCLUDE_INSTALL_DIR ${PROJECT_NAME}
RELOCATABLE "plugins"
)
SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON)
SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY FOLDER "plugins")
I've read in few places, this is one of them, that when using the meaning of the PUBLIC, PRIVATE and INTERFACE keywords in the context of commands such as target_include_directories is as follows:
PRIVATE - directories after this keyword will be added to the INCLUDE_DIRECTORIES property of the target specified.
INTERFACE - directories after this keyword will be added to the INTERFACE_INCLUDE_DIRECTORIES property of the target specified.
PUBLIC - directories after this keyword will be added to both.
Directories that will be added to the INTERFACE_INCLUDE_DIRECTORIES will be added to the INCLUDE_DIRECTORIES of any target dependent on the current target.
OK I run the following experiment:
// libmymath/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
add_library(MyMath src/mymath.cpp)
target_include_directories(MyMath PUBLIC include)
get_target_property(MYMATH_INC_DIR MyMath INCLUDE_DIRECTORIES)
get_target_property(MYMATH_INC_DIR_INTERFACE MyMath INTERFACE_INCLUDE_DIRECTORIES)
message("Libmath INCLUDE_DIRECTORIES: ${MYMATH_INC_DIR}")
message("Libmath INCLUDE_DIRECTORIES INTERFACE: ${MYMATH_INC_DIR_INTERFACE}")
calculator/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
add_executable(calculator calculator.cpp)
target_link_libraries(calculator MyMath)
get_target_property(CALCULATOR_INC_DIR calculator INCLUDE_DIRECTORIES)
get_target_property(CALCULATOR_INC_DIR_INTERFACE calculator INTERFACE_INCLUDE_DIRECTORIES)
message("Calculator INCLUDE_DIRECTORIES: ${CALCULATOR_INC_DIR}")
message("Calculator INCLUDE_DIRECTORIES INTERFACE: ${CALCULATOR_INC_DIR_INTERFACE}")
// top-level CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(Math)
add_subdirectory(libmath)
add_subdirectory(calculator)
get_target_property(CALC_INC_DIR calculator INCLUDE_DIRECTORIES)
get_target_property(CALC_INC_DIR_INTERFACE calculator INTERFACE_INCLUDE_DIRECTORIES)
message("From top-level CMakeLists.txt: Calculator INCLUDE_DIRECTORIES: ${CALC_INC_DIR}")
message("From top-level CMakeLists.txt: Calculator INTERFACE_INCLUDE_DIRECTORIES: ${CALC_INC_DIR_INTERFACE}")
Running this gives:
Libmath INCLUDE_DIRECTORIES: /home/yoav/playground/cmake/math/libmath/include
Libmath INCLUDE_DIRECTORIES INTERFACE: /home/yoav/playground/cmake/math/libmath/include
Calculator INCLUDE_DIRECTORIES: CALCULATOR_INC_DIR-NOTFOUND
Calculator INCLUDE_DIRECTORIES INTERFACE: CALCULATOR_INC_DIR_INTERFACE-NOTFOUND
From top-level CMakeLists.txt: Calculator INCLUDE_DIRECTORIES: CALC_INC_DIR-NOTFOUND
From top-level CMakeLists.txt: Calculator INTERFACE_INCLUDE_DIRECTORIES: CALC_INC_DIR_INTERFACE-NOTFOUND
So we see that the INCLUDE_DIRECTORIES property of calculator is not populated, although I can see in the flags.make file of this target that the CXX_INCLUDE variable is set properly !
What gives?
Content of target property INTERFACE_INCLUDE_DIRECTORIES is actually transferred into property INCLUDE_DIRECTORIES of every target, which consumes (with target_link_libraries command) given target.
But CMake defers all transfers to a consuming target until the generation phase, which occurs after whole CMakeLists.txt has been processed. This is why one cannot obtain final value of the property using get_target_property command: this command can only obtain current value of the property, before the transferring.
However, target properties referred by the generator expressions are expanded to the final value. (But again, this expansion is observable only at the generation phase and after it).
One way to see the expanded value of a generator expression is file(GENERATE): this command will create a file, which contains expanded generator expressions.
Example:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project (hello)
add_library(foo SHARED foo.c)
target_include_directories(foo PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_library(bar SHARED bar.c)
target_include_directories(bar PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include1)
target_link_libraries(bar PUBLIC foo)
get_target_property(bar_include_directories bar INCLUDE_DIRECTORIES)
message(STATUS "bar include directories: ${bar_include_directories}")
file(GENERATE
OUTPUT bar_include_directories.txt
CONTENT "include directories: $<TARGET_PROPERTY:bar,INCLUDE_DIRECTORIES>\n"
)
When configure this CMake project, only current value of the property will be printed:
build$ cmake ..
-- bar include directories: /home/tester/tests/cmake/include1
-- Configuring done
-- Generating done
-- Build files have been written to: /home/tester/tests/cmake/build
But the file bar_include_directories.txt will contain final value of the property:
$ cat bar_include_directories.txt
include directories: /home/tester/tests/cmake/include1;/home/tester/tests/cmake
Directories that will be added to the INTERFACE_INCLUDE_DIRECTORIES will be added to the INCLUDE_DIRECTORIES of any target dependent on the current target.
I believe this is a simple way to explain it that way, but indeed this is not something that can be observed inside cmake scripts, but it's rather "generated" when cmake generates the build system files.
target_link_libraries populates LINK_LIBRARIES and INTERFACE_LINK_LIBRARIES properties of a target. Then like after cmake has run all scripts and is generating build files, it goes through LINK_LIBRARIES of a target and takes INTERFACE_INCLUDE_DIRECTORIES of dependent targets and generates build system files that include these directories for that target.
When I generate the .vcxproj for my project with cmake 3.2.1, it generates the line <CompileAs>CompileAsCpp</CompileAs>
But when I try the cmake version 3.18.1, the generated .vcxproj file does not contain that previous line causing my tests to fail (the code compiles).
Here the cmake code which set the project properties :
add_library(${PROJECT_NAME} SHARED ${PROJECT_SRC})
if(CMAKE_BUILD_TOOL MATCHES "(msdev|devenv|nmake|MSBuild)")
target_compile_options(${PROJECT_NAME} PRIVATE "/TP")
endif()
target_include_directories(${PROJECT_NAME} PRIVATE ${PUBLIC_INCLUDES} ${PRIVATE_INCLUDES} )
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_target_properties(${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_target_properties(${PROJECT_NAME} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_target_properties(${PROJECT_NAME} PROPERTIES PDB_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
How can I ask to cmake to generate that missing line (<CompileAs>CompileAsCpp</CompileAs>)?
Thank you.
I tried to use CMake 3.18.0 and it worked.
The CompileAs tag was generated so I will use this version instead of the CMake 3.18.1
Thank you.
I have an IMPORTED SHARED library for and I'm linking with it via target_link_libraries (the library has IMPORTED_LOCATION set).
But then after installation in ldd output I see smth like:
path/on-dev-machine/to/libxxx.so => not found
instead of just
libxxx.so => path/on-testing-machine/to/libxxx.so
Why is that / how do I fix it? I'm adding lib paths to /etc/ld.so.conf.d
Sample code:
include(GNUInstallDirs)
function(add_and_install_lib lib_name location external_dep)
if(${location} MATCHES ".*\\.so")
add_library(${lib_name} SHARED IMPORTED) # MODULE treated as shared
else()
add_library(${lib_name} STATIC IMPORTED)
endif()
set_property(TARGET ${lib_name} PROPERTY IMPORTED_LOCATION ${location})
add_dependencies(${lib_name} ${external_dep})
endfunction()
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_LIBDIR}") # this doesn't seem to help
If anyone cares, it was the IMPORTED_NO_SONAME property (absence of it set to TRUE) of each imported lib that forced the full path to be taken.
Also CMAKE_SKIP_RPATH and CMAKE_SKIP_INSTALL_RPATH are useful in my opinion to make sure you have clean runtime paths (not straightly related to the question but still).
I have a custom target that is in fact an externally generated library that I want to integrate in my build.
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/liblib2.a
COMMAND make -f ${CMAKE_CURRENT_SOURCE_DIR}/makefile liblib2.a)
add_custom_target(lib2
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/liblib2.a)
How can I tell cmake that this target is in fact a library, where it can be found and where are the headers ?
To be clear : I don't want the upper CMakeList using this library having to manually specify include folders and the library location folder It must be done automatically (from the target properties).
On a standard cmake library I would just have to add the INTERFACE_INCLUDE_DIRECTORIES property in the library CMakeLists to make cmake link my app with the relevant -I and -L gcc parameters :
set_target_properties(lib1
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES
${CMAKE_CURRENT_SOURCE_DIR})
But in the case of a custom target I don't know how to to it.
Any clue ?
Thanks for your help.
Thanks to zaufi it works!
For others who may be interested in embedded externally build target inside cmake here is what I did :
cmake_minimum_required(VERSION 2.8)
SET(LIB_FILE ${CMAKE_CURRENT_SOURCE_DIR}/bin/liblib2.a)
SET(LIB_HEADER_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/include)
# how to build the result of the library
add_custom_command(OUTPUT ${LIB_FILE}
COMMAND make
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# create a target out of the library compilation result
add_custom_target(lib2_target DEPENDS ${LIB_FILE})
# create an library target out of the library compilation result
add_library(lib2 STATIC IMPORTED GLOBAL)
add_dependencies(lib2 lib2_target)
# specify where the library is and where to find the headers
set_target_properties(lib2
PROPERTIES
IMPORTED_LOCATION ${LIB_FILE}
INTERFACE_INCLUDE_DIRECTORIES ${LIB_HEADER_FOLDER})
Now in a CMakeLists.txt I can do somthing like
add_subdirectory(${ROOT_DIR}/lib1 bin/lib1)
add_subdirectory(${ROOT_DIR}/lib2 bin/lib2)
add_executable(app app.c )
target_link_libraries(app lib1 lib2)
No need to specify where the .a and the .h are.
You can use add_library() and tell that it actually imported. Then, using set_target_properties() you can set required INTERFACE_XXX properties for it. After that, you can use it as an ordinal target like every other built by your project.
Thank you for posting the solution. I have wrapped your snippet in a function:
function(add_external_library)
set(options)
set(oneValueArgs TARGET WORKING_DIRECTORY OUTPUT COMMENT)
set(multiValueArgs COMMAND INCLUDE_DIRS)
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" ${multiValueArgs}" ${ARGN})
# Specify how to build the result of the library
add_custom_command(OUTPUT "${ARGS_OUTPUT}"
COMMAND ${ARGS_COMMAND}
WORKING_DIRECTORY "${ARGS_WORKING_DIRECTORY}"
COMMENT "${ARGS_COMMENT}")
# Create a target out of the library compilation result
add_custom_target(${ARGS_TARGET}_target DEPENDS ${ARGS_OUTPUT})
# Create an library target out of the library compilation result
add_library(${ARGS_TARGET} STATIC IMPORTED GLOBAL)
add_dependencies(${ARGS_TARGET} ${ARGS_TARGET}_target)
# Specify where the library is and where to find the headers
set_target_properties(${ARGS_TARGET}
PROPERTIES
IMPORTED_LOCATION "${ARGS_OUTPUT}"
INTERFACE_INCLUDE_DIRECTORIES "${ARGS_INCLUDE_DIRS}")
endfunction()
# Example
add_external_library(TARGET YourLib
COMMAND /bin/bash compile_your_lib.sh
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT "output/yourlib.a"
INCLUDE_DIRS "include/a" "include/b"
COMMENT "Building YourLib")
add_executable(YourExe)
target_link_libraries(YourExe YourLib)