I porting a big qmake project to cmake and I have multiple linking problems, LNK2001, LNK2019 like:
foo.cpp.obj : error LNK2001: unresolved external symbol "public: signed char __cdecl libA::XXX::YYY(void)const "
(?YYY#XXX#libA##QEBACXZ)
foo.cpp.obj : error LNK2019: unresolved external symbol "public: unsigned short __cdecl libA::XXX::YYY(void)const " (?YYY#XXX#libA##QEBAGXZ) referenced in function "void __cdecl libA::ZZZZ(class LibA::WWWW &)" (?ZZZZ#libA##YAXAEAVVVVV#1##Z)
I think that the main problem is that I have libraries that are used in the main project as well in other libraries. But I don't discard the fact that in target_include_directories I'm adding a folder with the same name.
Example of my structure:
Project (Main):
find_package(Qt5 COMPONENTS core REQUIRED)
add_executable(main...
target_include_directories(main...
target_link_libraries(main PUBLIC
Qt5::Core
libA
libB
)
Project (LibA):
find_package(Qt5 COMPONENTS core REQUIRED)
add_library(libA...
target_include_directories(libA PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(main PUBLIC
Qt5::Core
libB
libC
)
Project (LibB):
find_package(Qt5 COMPONENTS core REQUIRED)
add_library(libB...
target_include_directories(libB PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(libB PUBLIC
Qt5::Core
libA
)
Project (LibC):
find_package(Qt5 COMPONENTS core REQUIRED)
add_library(libC...
target_include_directories(libC PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(libC PUBLIC
Qt5::Core
libA
libB
)
I'm missing something or doing something not properly??
Related
I am trying to find_package(foo), then link to it:
find_package(foo)
add_executable(bar ...)
target_link_libraries(bar PRIVATE fooStatic)
but when I try to build, I get:
CMake Error at CMakeLists.txt:58 (add_library):
Target "bar" links to target "gpgme::gpgme" but the target was
not found. Perhaps a find_package() call is missing for an IMPORTED
target, or an ALIAS target is missing?
foo itself is another project which has been installed. foo/CMakeLists.txt looks like this:
find_package(gpgme)
add_library(fooObjects OBJECT ...)
add_library(foo SHARED $<TARGET_OBJECTS:fooObjects>)
add_library(fooStatic STATIC $<TARGET_OBJECTS:fooObjects>)
target_link_libraries(foo PUBLIC fooObjects PRIVATE gpgme::gpgme)
target_link_libraries(fooStatic PUBLIC fooObjects PRIVATE gpgme::gpgme)
...
install(TARGETS foo fooStatic fooObjects EXPORT fooTargets ...)
export(EXPORT fooTargets FILE fooTargets.cmake)
configure_package_config_file(fooConfig.cmake.in fooConfig.cmake ...)
...
The generated fooTargets.cmake (included by fooConfig.cmake) contains:
add_library(foo SHARED IMPORTED)
set_target_properties(foo PROPERTIES
INTERFACE_LINK_LIBRARIES "fooObjects"
)
add_library(slibStatic STATIC IMPORTED)
set_target_properties(fooStatic PROPERTIES
INTERFACE_LINK_LIBRARIES "fooObjects;\$<LINK_ONLY:gpgme::gpgme>"
)
Why is gpgme::gpgme listed as an INTERFACE_LINK_LIBRARIES for the static version of the library? Is there a way to avoid this? I tried linking foo to a gpgme static target, but that didn't change the problem.
I know I could add find_dependency(gpgme) to my fooConfig.cmake.in, but it would be nice if my users didn't have to install libgpgme-dev to link against my project.
I have 2 static libraries in two different folders: libA and libB
libB must include libA
My main CMakeLists.txt is:
add_subdirectory(libA)
add_subdirectory(libB)
My first mistake was to think linking libA in libB would include it but it isn't:
target_link_libraries(${PROJECT_NAME} PUBLIC libA::libA)
I get undefined reference to some libA's functions when I try to use libB in an app.
How can I tell CMake to include libA as part of libB?
What's the best practice for that?
I'd like to avoid any extra step (How to merge two "ar" static libraries into one?)
If you control the builds for both libA and libB, you can solve this by creating OBJECT libraries libA-obj and libB-obj. You would then link libA-obj to libA and then link both object libraries to libB.
Here's a more detailed sketch
cmake_minimum_required(VERSION 3.22)
project(example)
# ...
# Can be in subdirectory
add_library(libA-obj ${libA-srcs})
add_library(libA)
target_link_libraries(libA PUBLIC libA-obj)
# Can be in subdirectory
add_library(libB-obj ${libB-srcs})
add_library(libB)
target_link_libraries(libB PUBLIC libB-obj libA-obj)
# Can be in parent directory
add_executable(app ${app-srcs})
target_link_libraries(app PRIVATE libB)
You can add the object files that comprise libA to the sources of libB to include them:
target_sources(LibB PRIVATE $<TARGET_OBJECTS:LibA>)
I am building a couple of 3rd party libraries for integration.
There are two different source codes for two libs, libA and libB.
The libA has following, in its CMakeLists.txt
project(libA)
...
add_library(${PROJECT} ${SRCS})
target_link_library(${PROJECT} DOESNOTMATTER)
target_compile_definitions(${PROJECT_NAME} PUBLIC LETS_USE_GORILLA)
libA compilation is successful separately as shared lib.
libB is dependent on libA.
While compiling libB, it is expected that public compile definition of libA are automatically taken care by cmake to be available in libB. But they are not.
Now, if I add below target_compile_definitions in CMakeLists.txt of libB, it compiles successfully.
project(libB)
...
add_library(${PROJECT} ${SRCS})
target_link_library(${PROJECT} libA)
target_compile_definitions(${PROJECT_NAME} PUBLIC LETS_USE_GORILLA)
But, as this is a 3rd party code, I am not allowed to change the CMakeLists.txt.
Question - How can I pass some arguments in cmake command line to change the COMPILE_DEFINITIONS
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)
I have a CMake project that builds a static library which depends on another static library. I would like to turn this static library into an object libraries. When I do that I get a compiler error and I imagine that there is something I don't get about object libraries.
Here is an example of what I am trying to achieve. MyLib and MyLib2 are both static libraries and MyLib uses a function defined and declared in MyLib2.
MyLib
CMakeList.txt
MyLib.h
MyLib.cpp
MyLib2
CMakeList.txt
MyLib2.h
MyLib2.cpp
MyLib2/CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project (MyLib2)
add_library(${PROJECT_NAME} OBJECT MyLib2.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
MyLib/CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project (MyLib)
add_subdirectory(MyLib2)
add_library(${PROJECT_NAME} STATIC MyLib.cpp MyLib.h)
target_link_libraries(${PROJECT_NAME} MyLib2)
MyLib.h includes MyLib2.h to use the function it declares.
#ifndef MyLib
#define MyLib
#include "MyLib2.h"
#endif
When MyLib2 is built as a static library I can build the code without any problems (I am using make and clang on Mac). However when I turn MyLib2 into an object library I get a compile error saying that MyLib2.h cannot be found.
MyLib.h:4:10: fatal error: 'MyLib2.h'file not found
Here are the content of the CMake files when MyLib2 is an object library.
MyLib2/CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project (MyLib2)
add_library(${PROJECT_NAME} OBJECT MyLib2.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
MyLib/CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project (MyLib)
add_subdirectory(MyLib2)
add_library(${PROJECT_NAME} STATIC MyLib.cpp $<TARGET_OBJECTS:MyLib2>)
I do not understand why MyLib can no longer MyLib2.h when MyLib2 is an object library. Maybe there is something wrong with the way is use target_include_directories.
This was answered in the comments:
#Clem: When you link with libary target, you consume its INTERFACE_INCLUDE_LIBRARIES property, which contains include directory Mylib2. When you use object library via $, you don't use any target, so don't consume any property. – Tsyvarev