Cannot specify compile options for imported target "..." - cmake

I want to provide the users of my library with two targets: one that specifies the include path etc., and one that carries useful extra compile options. However, for the extra target some of my users are getting the error
Cannot specify compile options for imported target "myproject::extra"
so it seems on older CMake versions.
I tested with CMake 3.9.2. The test project, including CI is on GitHub, with failing build here.
(How) can my approach be rendered robust for all CMake versions?
The project's main CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project(myproject)
add_library(myproject INTERFACE)
set(MYPROJECT_VERSION "1.0.0")
target_include_directories(myproject INTERFACE
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" DESTINATION include)
install(TARGETS myproject EXPORT myproject-targets)
install(EXPORT myproject-targets FILE myprojectTargets.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/myproject")
write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/myprojectConfigVersion.cmake" VERSION ${MYPROJECT_VERSION} COMPATIBILITY AnyNewerVersion)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/myprojectConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/myprojectConfigVersion.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/myproject")
The project's myprojectConfig.cmake:
include(CMakeFindDependencyMacro)
if(NOT TARGET myproject)
include("${CMAKE_CURRENT_LIST_DIR}/myprojectTargets.cmake")
endif()
if(NOT TARGET myproject::extra)
add_library(myproject::extra INTERFACE IMPORTED)
if(MSVC)
target_compile_options(myproject::extra INTERFACE /W4)
else()
target_compile_options(myproject::extra INTERFACE -Wall)
endif()
endif()
The user's project CMakeLists.txt could then look as follows:
cmake_minimum_required(VERSION 3.0)
project(myexec)
find_package(myproject REQUIRED)
add_executable(myexec main.cpp)
target_link_libraries(myexec PRIVATE myproject myproject::extra)

List of functions applicable for IMPORTED and INTERFACE targets changes as CMake evolves.
Most of such functions affects only on specific target properties. So, instead of calling a function, you may set the property directly. This will work in any CMake version:
# Works only in new CMake versions
target_compile_options(myproject::extra INTERFACE /W4)
# Equivalent which works in any CMake version
set_property(TARGET myproject::extra PROPERTY INTERFACE_COMPILE_OPTIONS /W4)

Related

CMake Submodules Do Not Resolve Project Dependencies on MSVC "Cannot open include files"

I am using the following cmake file:
cmake_minimum_required(VERSION 3.19)
project(Neon LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
option(LIBIGL_WITH_OPENGL "Use OpenGL" ON)
option(LIBIGL_WITH_OPENGL_GLFW "Use GLFW" ON)
option(LIBIGL_WITH_OPENGL_GLFW_IMGUI "Use ImGui" ON)
include(libigl)
find_package(Boost REQUIRED COMPONENTS unit_test_framework)
add_executable(Neon src/main.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}}/include>
$<INSTALL_INTERFACE:include>)
target_link_libraries(Neon PUBLIC Eigen3::Eigen igl::core igl::opengl_glfw solvers)
add_subdirectory(solvers)
I have my main executable, and I have my subdirectory "solvers" which has some other numerical routines. The CMake file for that is as follows:
project(solvers)
add_library(${PROJECT_NAME} SHARED src/LinearElastic.cpp)
target_include_directories(${PROJECT_NAME}
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src)
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Config
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
target_link_libraries(${PROJECT_NAME} igl::core Eigen3::Eigen)
install(DIRECTORY include/ DESTINATION "${INSTALL_INCLUDE_DIR}")
install(EXPORT ${PROJECT_NAME}Config DESTINATION share/${PROJECT_NAME}/cmake)
export(TARGETS ${PROJECT_NAME} FILE ${PROJECT_NAME}Config.cmake
Eigen and IGL are both libraries which are found from the following:
if(TARGET igl::core)
return()
endif()
include(FetchContent)
FetchContent_Declare(
libigl
GIT_REPOSITORY https://github.com/libigl/libigl.git
GIT_TAG v2.3.0
)
# Note: In libigl v3.0.0, the following will become a one-liner:
# FetchContent_MakeAvailable(libigl)
FetchContent_GetProperties(libigl)
if(NOT libigl_POPULATED)
FetchContent_Populate(libigl)
endif()
list(PREPEND CMAKE_MODULE_PATH "${libigl_SOURCE_DIR}/cmake")
include(${libigl_SOURCE_DIR}/cmake/libigl.cmake)
Building the main executable has no issues whatsoever, however, when attempting to use the dependencies from the libigl library in the submodule, I am encountering a lot of problems where the imports are unable to resolve. In particular, when attempting to use Eigen, I get "Cannot open include file" errors.
I have tried googling this issue, but none seem to cover the case when I am using a custom-written include() for a library. Ordinarily I'd just find_package in the submodules, but this doesn't seem to work correctly. I assume this is something silly, or perhaps I'm misunderstanding. Please let me know if I can improve the clarity of the question.
The particular error is here:
#ifndef NEON_LINEARELASTIC_H
#define NEON_LINEARELASTIC_H
#include <Eigen/Dense> // "Cannot include" error
class LinearElastic {
public:
Eigen::MatrixXi foobar;
auto doIt() -> void;
};
#endif//NEON_LINEARELASTIC_
I was able to solve this problem. I am typically used to a unix-based environment, so when attempting to apply the same typical thinking to windows, I encountered a few issues, namely, the way different compilers handle shared linking. To avoid rehashing what is already known, I defer to this post to allow anyone else encountering this issue to solve the problem.
I changed the CMakeLists.txt for my main executable to the following:
cmake_minimum_required(VERSION 3.19)
project(Neon LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(INSTALL_LIB_DIR lib CACHE PATH "Install directory for library code")
set(INSTALL_BIN_DIR CACHE PATH "Install directory for executables")
set(INSTALL_INCLUDE_DIR include CACHE PATH "Install directory for header files")
list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
option(LIBIGL_WITH_OPENGL "Use OpenGL" ON)
option(LIBIGL_WITH_OPENGL_GLFW "Use GLFW" ON)
option(LIBIGL_WITH_OPENGL_GLFW_IMGUI "Use ImGui" ON)
include(libigl)
if(MSVC) // This line fixed it!
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
set(BUILD_SHARED_LIBS TRUE)
endif()
foreach(p LIB BIN INCLUDE CMAKE)
set(var INSTALL_${p}_DIR)
if(NOT IS_ABSOLUTE "${${var}}")
set(${var} "${CMAKE_INSTALL_PREFIX}/${${var}}")
endif()
endforeach()
find_package(Boost REQUIRED COMPONENTS unit_test_framework)
include_directories("${PROJECT_SOURCE_DIR}"
"${PROJECT_BINARY_DIR}")
add_executable(Neon src/main.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}}/include>
$<INSTALL_INTERFACE:include>)
target_link_libraries(Neon PRIVATE Eigen3::Eigen igl::core igl::opengl_glfw solvers)
add_subdirectory(solvers)
By setting the windows-specific options, the app was able to build and run without issue.

CMake: target not in export set

I have a library foo which depends on bar. bar provides a nice barConfig.cmake upon installation.
cmake_minimum_required(VERSION 3.7)
project(foo VERSION 1.0)
find_package(bar)
add_library(foo SHARED foo.c)
target_link_libraries(foo PUBLIC bar)
install(TARGETS foo EXPORT fooTargets LIBRARY DESTINATION lib)
export(EXPORT fooTargets FILE fooTargets.cmake)
install(EXPORT fooTargets FILE fooTargets.cmake DESTINATION lib/cmake)
This works great. We simply apt install bar-dev then compile foo.
But since these packages are both developed by us and are related, my team would like to develop them in the same IDE session and compile them at the same time. I want to allow that, but I don't want to change the fact that these are already deployed as seperate packages and can be built independently.
if (DIRECTORY_EXISTS ../bar)
set(BAR_NO_INSTALL ON)
set(BAR_SKIP_TESTS ON)
add_subdirectory(../bar ${CMAKE_CURRENT_BINARY_DIR}/bar)
else()
find_package(bar)
endif()
If ../bar/ exists, then this results in:
CMake Error: install(EXPORT "fooTargets" ...) includes target "foo" which
requires target "bar" that is not in the export set.
How can I prevent the need to export bar in the foo package?
I'm trying to figure out why find_package(bar) works, but add_subdirectory(bar) doesn't. barConfig.cmake via barTargets.cmake defines bar like so:
add_library(bar SHARED IMPORTED)
set_property(TARGET bar APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
set_target_properties(bar PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "../bar/include"
IMPORTED_LOCATION_NOCONFIG "build/libbar.so.1.0.13574"
IMPORTED_SONAME_NOCONFIG "libbar.so.1"
)
My guess was the IMPORTED property that is used when bar is created in the autogenerated barConfig.cmake. I tried adding setting property (below), but saw no differences:
set_target_properties(bar PROPERTIES IMPORTED TRUE)
fooConfig.cmake does include the following which creates target bar (not bar::bar).
find_depdendency(bar)
I tried the following aliasing, hoping that an alias' inability to be exported would help me. But it still expects me to export bar.
add_library(barImported ALIAS bar)
target_link_libraries(foo PUBLIC barImported)
I tried linking only the build interface, hoping that bar would not need to be exported when installed. But that also had no effect.
target_link_libraries(foo PUBLIC $<BUILD_INTERFACE:bar>)
How can I prevent the need to export bar in the foo package?
You can't, at least not without hacks (see below).
I'm trying to figure out why find_package(bar) works, but add_subdirectory(bar) doesn't. barConfig.cmake via barTargets.cmake defines bar like so:
Because in one case, the target is imported (which can only be set on creation) and in the other case, the target is normal. Normal targets must be installed if normal targets that depend on them are installed.
The semantic issue with add_subdirectory is that CMake believes that any code you add this way is first-party. I have found that add_subdirectory and its relative FetchContent are more trouble than they're worth for installed dependencies. They're useful for build-or-test-only dependencies that do not need to be shipped.
One way to work around this is to use the COMPONENT / NAMELINK_COMPONENT features of the install() command. Then you would set the CPACK_COMPONENTS_ALL variable to include only the components from foo and not those from bar. When specifying components, you should avoid generic names like development or runtime, and instead prefix those names with the project name: foo_development, bar_runtime, etc.
Unfortunately, this does not affect the cmake --build . --target install command. You'll have to go through cmake --install . --component <comp> or CPack.
A hack is to add the bar dependency to foo as $<BUILD_INTERFACE:...> and then re-attach it to foo in fooConfig.cmake after include-ing your generated target export file and finding bar via find_dependency.
Here's the solution I came up with. This is for an arbitrary library foo (dynamic and static versions plus tests) which could also be a dependency in the same manner that I import bar:
cmake_minimum_required(VERSION 3.7)
include(GNUInstallDirs)
project(foo VERSION 1.0)
# Options set to
# ON explicitly for build machines,
# OFF by default for developers who build dependencies inline and only build for local use
option(FOO_EXPORT_TARGETS "Generate fooConfig.cmake for packaging." OFF)
option(FOO_BUILD_TESTS "Build and run tests. Default off for aircraft. Turn on in your IDE if developing foo" OFF)
# This is best implemented as a macro/function from an included cmake
set(BAR_ROOT "/noexist" CACHE PATH "Location of BAR for in-source building")
if (DIRECTORY_EXISTS "${BAR_ROOT}")
add_subdirectory(${BAR_ROOT})
else()
find_package(bar)
endif()
set(fooSources foo.c)
set(fooPublicHeaders include/foo.h)
add_library(fooObjects OBJECT ${fooSources})
target_include_directories(fooObjects
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
PRIVATE
$<TARGET_PROPERTY:bar,INTERFACE_INCLUDE_DIRECTORIES>
)
add_library(foo SHARED $<TARGET_OBJECTS:fooObjects>)
add_library(fooStatic STATIC $<TARGET_OBJECTS:fooObjects>)
target_link_libraries(foo PUBLIC fooObjects bar)
target_link_libraries(fooStatic PUBLIC fooObjects barStatic)
set_target_properties(fooObjects foo fooStatic
PROPERTIES
POSITION_INDEPENDENT_CODE ON
)
if (NOT FOO_EXPORT_TARGETS)
install(TARGETS foo LIBRARY DESTINATION lib)
else()
set_target_properties(foo fooStatic
PROPERTIES
VERSION ${foo_VERSION}
SOVERSION ${foo_VERSION_MAJOR}
PUBLIC_HEADER "${fooPublicHeaders}"
)
install(
TARGETS foo fooStatic fooObjects
EXPORT fooTargets
RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/foo
)
include(CMakePackageConfigHelpers)
set(configDir "${CMAKE_INSTALL_FULL_LIBDIR}/cmake/foo-${foo_VERSION_MAJOR}")
configure_package_config_file(
"${CMAKE_CURRENT_LIST_DIR}/fooConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/fooConfig.cmake"
INSTALL_DESTINATION ${configDir}
)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/fooConfigVersion.cmake"
VERSION ${foo_VERSION}
COMPATIBILITY SameMajorVersion
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/fooConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/fooConfigVersion.cmake"
DESTINATION ${configDir}
)
export (EXPORT fooTargets FILE fooTargets.cmake)
install(EXPORT fooTargets FILE fooTargets.cmake DESTINATION ${configDir})
endif()
if (FOO_BUILD_TESTS)
add_executable(test_foo test.c)
target_link_libraries(test_foo foo)
enable_testing()
add_test(NAME test_foo COMMAND test_foo WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test)
endif()
With the following fooConfig.cmake.in:
include(CMakeFindDependencyMacro)
#PACKAGE_INIT#
find_dependency(bar)
if(NOT TARGET foo)
include("${CMAKE_CURRENT_LIST_DIR}/fooTargets.cmake")
endif()
The build machines create a debian package out of this. This is what the debian/rules files look like to ensure it is compiled without any in-source building, with packaging, and with tests:
#!/usr/bin/make -f
#export DH_VERBOSE = 1
%:
dh $#
override_dh_auto_configure:
dh_auto_configure -- \
-DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) \
-DFOO_BUILD_TESTS=ON \
-DFOO_EXPORT_TARGETS=ON \
-DBAR_ROOT=/noexist

CMake: link order of imported targets incorrect

I have the following scenario:
I import two prebuilt libraries into my project (libA, libB)
libB has a dependency on libA
The executable depends on both libA and libB
However, the relative linking order in my link.txt is incorrect
/usr/bin/c++ CMakeFiles/bin.dir/main.cpp.o -o bin ../libA.a ../libB.a
I would expect libA.a to be listed after libB.a.
The CMakeLists.txt looks something along the following lines
cmake_minimum_required(VERSION 3.13)
project(cmake_test)
set(lib_dir ${CMAKE_CURRENT_SOURCE_DIR})
add_library(MY::libA IMPORTED INTERFACE)
set_target_properties(MY::libA PROPERTIES INTERFACE_LINK_LIBRARIES "${lib_dir}/libA.a")
add_library(MY::libB IMPORTED INTERFACE)
set_target_properties(MY::libB PROPERTIES INTERFACE_LINK_LIBRARIES "MY::libA;${lib_dir}/libB.a")
add_executable(bin ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
target_link_libraries(bin PUBLIC MY::libB MY::libA)
Below a description of my attempts to solve the problem. Some without success and some with sucecss but using modifications that render the code usless for the production environment.
Successful attempts:
Remove the depedency of bin on libA (i.e. replace the last line by target_link_libraries(bin PUBLIC MY::libB). This works but I cannot remove the dependency in real code.
Replace the target type IMPORTED INTERFACE by IMPORTED STATIC. Use IMPORTED_LOCATION instead of INTERFACE_LINK_LIBRARIES and use target_link_libraries to express the dependency of libB on libA. In this case the link.txt yields: [...] -o bin ../libA.a ../libB.a ../libA.a. As soon as I revert the target type for libB the link order breaks down again. In the production environment, however, one of the targets is created by conan as IMPORTED INTERFACE.
Attempts without success (same behaviour as described):
Create a separate IMPORTED target (use IMPORTED_LOCATION) for every lib and group them inside an INTERFACE target
Sprinkle the code with ADD_DEPENDENCIES
Remove libA from the INTERFACE_LINK_LIBRARIES in line 9 and use target_link_libraries(MY::libB INTERFACE MY::libA) instead. Same result.
Example code that shows the same failure using INTERFACES as a building block
cmake_minimum_required(VERSION 3.13)
project(cmake_test)
set(lib_dir ${CMAKE_CURRENT_SOURCE_DIR})
# libA
add_library(MY::libA_file1 IMPORTED STATIC)
set_target_properties(MY::libA_file1 PROPERTIES IMPORTED_LOCATION "${lib_dir}/libA.a")
add_library(libA INTERFACE)
target_link_libraries(libA INTERFACE MY::libA_file1)
# libB
add_library(MY::libB_file1 IMPORTED STATIC)
set_target_properties(MY::libB_file1 PROPERTIES IMPORTED_LOCATION "${lib_dir}/libB.a")
add_library(libB INTERFACE)
target_link_libraries(libB INTERFACE MY::libB_file1 libA)
add_executable(bin ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
target_link_libraries(bin PUBLIC libA libB)
You incorrectly think about INTERFACE_LINK_LIBRARIES property as a "content" of the library's target, which is ordered by target_link_libraries call.
Using
target_link_libraries(MY::libB INTERFACE MY::libA)
you setup link dependency between library targets MY::libB and MY::libA. That is, "content" of MY::libB target should come before "content" of MY::libA target in the linking command line.
But INTERFACE_LINK_LIBRARIES property is NOT a "content" of the library target! It is just an additional link dependency.
As opposite, IMPORTED_LOCATION (for non-INTERFACE IMPORTED target) is a "content" of the library, and target_link_libraries affects on its ordering.
It seems that you cannot add link dependency for a library, using INTERFACE library target. You should use IMPORTED library target for that purpose:
# Collect libraries related to 'libA'
file(GLOB libs_A "${lib_dir}/libA*.a")
# For each library create IMPORTED target with IMPORTED_LOCATION property.
set(libs_A_targets)
foreach(lib_A ${libs_A})
# Form a unique name for the IMPORTED target: subtarget_A_*
string(REGEX REPLACE "^${lib_dir}/libA([^.]*).a$" "subtarget_A_\\1" lib_A_target ${lib_A})
# Create a target with this name
add_library(${lib_A_target} STATIC IMPORTED)
set_target_properties(${lib_A_target} PROPERTIES IMPORTED_LOCATION ${lib_A})
# And add the target into the list
list(APPEND libs_A_targets ${lib_A_target})
endforeach()
# In a similar way collect libraries for libB.
set(lib_B_targets ...)
# Now link each libB* library with each libA*.
foreach(lib_B_target ${libs_B_targets})
target_link_libraries(${lib_B_target} INTERFACE ${libs_A_targets})
endforeach()
# Now interface libraries, which combine libA* and libB*, can be created
add_library(libA INTERFACE)
target_link_libraries(libA INTERFACE ${libs_A_targets})
add_library(libB INTERFACE)
target_link_libraries(libB INTERFACE ${libs_B_targets})
# Now these INTERFACE libraries can be linked into an executable in any order
add_executable(bin ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
target_link_libraries(bin PUBLIC libA libB)

Provide pre-compiled library as install(TARGET ...) to other projects in CMake

I want to define a CMakeLists which is able to export a precompiled library to an out of source output folder.
This is how it currently look like:
cmake_minimum_required (VERSION 3.3)
project(duktape)
include_directories("./code" "./code")
file(GLOB allCodeFiles
"./code/*.h"
#"./code/*.cpp"
)
add_library(${PROJECT_NAME} SHARED IMPORTED)
set_property(TARGET ${PROJECT_NAME} PROPERTY IMPORTED_LOCATION ./bin/duktape.dll)
set_property(TARGET ${PROJECT_NAME} PROPERTY IMPORTED_IMPLIB ./lib/duktape.lib)
export(PACKAGE ${PROJECT_NAME})
Message(INSTALL_INCLUDE_DIR=${INSTALL_INCLUDE_DIR})
install(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
ARCHIVE DESTINATION "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}"
LIBRARY DESTINATION "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
PUBLIC_HEADER DESTINATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}/code"
)
My current understanding of the install(TARGETS ...) is that it moves my libs to my out of soruce output folder and shares the headers to the project (some binary maybe) which depends on the lib.
The problem is that the export than tells:
CMake Error at C:/Test1/duktape-1.4.0.win32/CMakeLists.txt:30 (install):
install TARGETS given target "duktape" which does not exist in this directory.
[Edit]:
I still found no solution to just define a "project" to link a pre-compiled lib for integration purpose as a dependency to other projects?! I saw this kind of question ~10 times on google etc. and still can't belive that i didnt found one clear easy answer for this generic thing...
This is what i now end up with trying:
cmake_minimum_required (VERSION 3.3)
project(duktape)
#include(${CMAKE_BINARY_DIR}/DebugCmakeStuff.wtf)
# https://cmake.org/cmake/help/v3.0/command/add_library.html
add_library(${PROJECT_NAME} SHARED IMPORTED GLOBAL)
set_target_properties(${PROJECT_NAME} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ./include)
set_target_properties(${PROJECT_NAME} PROPERTIES INTERFACE_COMPILE_DEFINITIONS "")
set_target_properties(${PROJECT_NAME} PROPERTIES INTERFACE_COMPILE_OPTIONS "")
set_target_properties(${PROJECT_NAME} PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll)
set_target_properties(${PROJECT_NAME} PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/lib/duktape.lib)
set_target_properties(${PROJECT_NAME} PROPERTIES IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll)
set_target_properties(${PROJECT_NAME} PROPERTIES IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/lib/duktape.lib)
set_target_properties(${PROJECT_NAME} PROPERTIES IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll)
set_target_properties(${PROJECT_NAME} PROPERTIES IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/lib/duktape.lib)
#target_link_libraries(${PROJECT_NAME} LINK_PUBLIC "duktape.lib")
#add_custom_target(${PROJECT_NAME}_Export MODULE)
#add_dependencies(${PROJECT_NAME} duktape)
#export(PACKAGE ${PROJECT_NAME})
#install(TARGETS ${PROJECT_NAME}
# #EXPORT ${PROJECT_NAME}_Targets
# RUNTIME DESTINATION "${EXECUTABLE_OUTPUT_PATH}"
# ARCHIVE DESTINATION "${LIBRARY_OUTPUT_PATH}"
# LIBRARY DESTINATION "${LIBRARY_OUTPUT_PATH}"
# PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/${PROJECT_NAME}/include")
#install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll DESTINATION "${EXECUTABLE_OUTPUT_PATH}")
#install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/lib/duktape.lib DESTINATION "${LIBRARY_OUTPUT_PATH}")
#add_custom_command(TARGET ${PROJECT_NAME}
# PRE_LINK
# COMMAND copy ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll "${EXECUTABLE_OUTPUT_PATH}"
# COMMAND copy ${CMAKE_CURRENT_SOURCE_DIR}/lib/duktape.lib "${LIBRARY_OUTPUT_PATH}"
# )
#add_custom_command(OUTPUT duktape.dll
# COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll ${EXECUTABLE_OUTPUT_PATH}/duktape.dll
# COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll ${EXECUTABLE_OUTPUT_PATH}/duktape.dll
# COMMAND "copy ${CMAKE_CURRENT_SOURCE_DIR}/lib/duktape.lib ${LIBRARY_OUTPUT_PATH}"
# )
So after 2 days wasting time with CMake and its crashes... i just realized that i'm most propably 90% fast, if i would have write the build files manually...
A solution would be nice
Command flow install(TARGETS) installs only targets built within project. It doesn't install imported targets. See, e.g., this bugreport.
For install imported libraries you may use install(FILES) command flow:
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll
DESTINATION <...>)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.lib
DESTINATION <...>)
(In your code you install into CMAKE_RUNTIME_OUTPUT_DIRECTORY and similar dirs, which refers to build tree. Never install under build tree!)

custom target as a target library in cmake

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)