How to create a shared library using object library in CMake - c++

I have two shared libraries each one having its own CMakeLists.txt. The directory structure is like this.
main_dir
|--- subdir
| |--- src1.cpp
| |--- src2.cpp
| |--- src3.cpp
| |--- CMakeLists.txt
|--- src11.cpp
|--- CMakeLists.txt
Currently, I am able to build both main library and sub library (say main.so and sub.so).
The CMakeLists.txt for both looks as below.
main_dir/CMakeLists.txt
option(BUILD_SHARED_LIBS "Build the shared library" ON)
if(BUILD_SHARED_LIBS)
add_library(mainlib SHARED)
endif()
target_sources(mainlib PRIVATE
src11.cpp
)
add_subdirectory(subdir)
subdir/CMakeLists.txt
option(BUILD_SHARED_LIBS "Build the shared library" ON)
if(BUILD_SHARED_LIBS)
add_library(sublib SHARED)
endif()
target_sources(sublib PRIVATE
src1.cpp
src2.cpp
src3.cpp)
Now I want the object file or symbols of sub library to be included in the main library as well, so that users can still use the main library alone even if they don't link their application to the sub library.
I'm new to CMake and I was trying to create an object library out of all source files in the sub_dir and link this to my mainlib.
add_library(subarchive OBJECT src1.cpp src2.cpp src3.cpp)
target_sources(mainlib INTERFACE $<TARGET_OBJECTS:subarchive>)
But It gives me error.
(add_library) No SOURCES given to target: mainlib
How can I create an object library in sub_dir and add it to both sublib and mainlib. Is there any better way to do this. Thanks.

To use an OBJECT library, link with it. That doesn't do any actual linking -- there is no "thing" that is the object library, it's just a collection of object files that CMake knows about -- but puts the object files in the target:
target_link_libraries(mainlib PRIVATE sublib)
Here is a complete example (it creates source files one.cpp and two.cpp so that it is entirely self-contained):
cmake_minimum_required(VERSION 3.18)
project(example)
if(NOT EXISTS one.cpp)
file(WRITE one.cpp "int x;")
endif()
if(NOT EXISTS two.cpp)
file(WRITE two.cpp "int y;")
endif()
add_library(sublib OBJECT one.cpp)
add_library(mainlib SHARED two.cpp)
target_link_libraries(mainlib PRIVATE sublib)

I was able to get it working by setting PARENT_SCOPE for the object library in the suddirectory. The modifications to the original code look like this.
main_dir/CMakeLists.txt
set(SUBARCHIVE_OBJECTS)
add_subdirectory(subdir)
target_link_libraries(mainlib private ${SUBARCHIVE_OBJECTS}
subdir/CMakeLists.txt
add_library(subarchive OBJECT src1.cpp src2.cpp src3.cpp)
list(APPEND SUBARCHIVE_OBJECTS ${TARGET_OBJECTS:subarchive>)
set(SUBARCHIVE_OBJECTS ${SUBARCHIVE_OBJECTS} PARENT_SCOPE)
Thanks for the responses.

Related

Simple multiple directories CMake

I just would like to create my CMakeLists.txt like this, but the CMake guide is not that user friendly for beginners, I have already researched too much, do not downvote, please.
I have seen some solutions including in the add_executable() every source file manually, but I would like to make it more generic. Also, I would not like to use the file(GLOB SOURCES *.cpp *.h).
By the way, I am just attempting to create a Unix Makefile with the command
cmake -G 'Unix Makefiles'..
How can I do this?
CMakeLists.txt
build/
...
project/
CMakeLists.txt
Inc/
CMakeLists.txt
*.h
Src/
CMakeLists.txt
main.cpp
*.cpp
I have so far for the main directory/CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(my_project)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++2a")
add_subdirectory(project)
for the project/CMakeLists.txt
add_subdirectory(Inc)
add_subdirectory(Src)
add_executable(exec ${Sources})
target_link_libraries(exec ${Headers})
For the Inc/CMakeLists.txt
add_library(
Headers
H1.h
H2.h
...
Hn.h
)
and for the Src/CMakeLists.txt
set(Sources main.cpp
Src1.cpp
Src2.cpp
...
Srcn.cpp
)
As it is so far, it throws the following error
-- Version: 7.0.3
-- Build type:
-- CXX_STANDARD: 11
-- Required features: cxx_variadic_templates
-- Configuring done
CMake Error: Cannot determine link language for target "exec".
CMake Error: CMake can not determine linker language for target: exec
CMake Error: Cannot determine link language for target "Headers".
CMake Error: CMake can not determine linker language for target: Headers
-- Generating done
CMake Generate step failed. Build files cannot be regenerated correctly.
add_library(
Headers
H1.h
H2.h
does not add a include directory to search path. It only adds the headers to sources - and because headers are not compiled, it effectively does nothing. You can do that with target_include_directories. Because cmake doesn't know if these are C or C++ headers, it cannot determine the link language.
add_subdirectory(Src)
add_executable(exec ${Sources})
add_subdirectory introduces a new "scope". Variables set in add_subdirectory are not visible outside, unless you set them with PARENT_SCOPE like set(var "value" PARENT_SCOPE). ${Sources} is effectively empty, so it does add_executable(exec) with no sources, so cmake cannot determine the link language.
target_link_libraries(exec ${Headers})
A target is not a variable. ${Headers} is empty. To link with a target just use it's name.
I also subjectively advice do not use mixed case variables (and folder names).
You could:
# project/CMakeLists.txt
include(Inc/CMakeLists.txt)
include(Src/CMakeLists.txt)
add_executable(exec ${exec_sources} ${exec_headers})
target_include_directories(exec PUBLIC Inc)
# Inc/CMakeLists.txt
set(exec_headers
Inc/H1.h
Inc/H2.h
...
Inc/Hn.h
)
# Src/CMakeLists.txt
set(exec_sources
Src/main.cpp
Src/Src1.cpp
Src/Src2.cpp
...
Src/Srcn.cpp
)
# Instead of `Src/` in front you could write a `foreach()` loop.
but I do not see a point in having so many CMakeLists.txt. Just have one file:
# project/CMakeLists.txt
include(Inc/CMakeLists.txt)
include(Src/CMakeLists.txt)
add_executable(exec
Src/main.cpp
Src/Src1.cpp
Src/Src2.cpp
...
Src/Srcn.cpp
Inc/H1.h
Inc/H2.h
...
Inc/Hn.h
)
target_include_direcotories(exec PUBLIC Inc)
Och, there is also target_sources you could use, so you could also do it like this I think:
# project/CMakeLists.txt
add_executable(exec ${exec_sources} ${exec_headers})
add_subdirectory(Inc)
add_subdirectory(Src)
# Inc/CMakeLists.txt
target_include_directories(exec PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_sources(exec PRIVATE
H1.h
H2.h
...
Hn.h
)
# Src/CMakeLists.txt
target_sources(exec PRIVATE
main.cpp
Src1.cpp
Src2.cpp
...
Srcn.cpp
)

How to generate a DLL linked to a static library with CMake

The main objective with this question is to write an CMakeLists.txt to generate a dynamic library, "containing/linked" a static library.
Let me create the scenario:
My C++ code is written in mycode.cpp
In mycode.cpp, I call some functions from libthirdparty.a (static library)
I want to generate libmylib.so (shared library) to be dynamically linked by others
libmylib.so must to "contain" libthirdparty.a
My attempt to write this script is at the lines bellow:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Werror -m64 -fPIC ")
add_executable(myapp mycode.cpp)
target_link_libraries(myapp thirdparty)
add_library(mylib SHARED myapp)
But of course this is not working and I would like some help to write it correctly.
For now, let's remove the myapp and focus only on the library you are trying to create.
That said, here is what you could do
cmake_minimum_required(VERSION 3.12)
project(AwesomeLib)
include(GenerateExportHeader)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
find_package(ThirdParty REQUIRED)
add_library(mylib SHARED mylib.cpp)
target_link_libraries(mylib PUBLIC ThirdParty)
# Note: If you library will be cross-platform, flag should be conditionally specified. Have a look at generator-expression
target_compile_options(mylib PRIVATE -Wall -Werror)
generate_export_header(mylib)
# TODO:
# * add install rules
# * generate config-file package
# * add tests
Notes:
generate_export_header will generate "mylib_export.h" header with the MYLIB_EXPORT macro for exporting symbols. See https://cmake.org/cmake/help/latest/module/GenerateExportHeader.html
to understand the idea behind find_package(ThirdParty REQUIRED), I recommend you read config-file package. See Correct way to use third-party libraries in cmake project
to learn more about generator expression. See https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html

CMake append objects from different CMakeLists.txt into one library

I would like to create a single library from objects from multiple sub-directories, each one containing their own CMakeLists.txt with OBJECT library trick to have multiple targets with different compile options.
Here are the files:
project_dir
|--- subdir1
| |--- src1.c
| |--- CMakeLists.txt
|--- subdir2
| |--- src2.c
| |--- CMakeLists.txt
|--- CMakeLists.txt
Contents of all CMakeLists.txt
// project_dir/CMakeLists.txt
// what to put here? to make one single library (mainLib)
// project_dir/subdir1/CMakeLists.txt
add_library(lib11 OBJECT src1.c)
set_target_properties(lib11 PROPERTIES COMPILE_FLAGS "some-flags11")
add_library(lib12 OBJECT src1.c)
set_target_properties(lib12 PROPERTIES COMPILE_FLAGS "some-flags12")
// here I would like to add lib11:objects and lib12:objects to mainLib
// how should it be done?
// project_dir/subdir2/CMakeLists.txt
// *** similar to subdir1 but with src2.c that creates lib21 and lib22
// here I would like to add lib21:objects and lib22:objects to mainLib
// how should it be done?
Can it be done platform independently? Thanks.
EDIT:
Based on CMake: how create a single shared library from all static libraries of subprojects?, I can change the cmake files to the following, but still doesn't solve my problem.
// project_dir/subdir1/CMakeLists.txt
add_library(lib11 OBJECT src1.c)
set_target_properties(lib11 PROPERTIES COMPILE_FLAGS "some-flags11")
add_library(lib12 OBJECT src1.c)
set_target_properties(lib12 PROPERTIES COMPILE_FLAGS "some-flags12")
add_library(lib1 STATIC $<TARGET_OBJECTS:lib11> $<TARGET_OBJECTS:lib12>)
// Above add_library cannot be OBJECT which would fix my problem.
// how to append the lib1 (lib11 and lib12) to mainLib?
EDIT:
Updating my post with attempt to chain interface libraries as suggested in the answer.
add_library(lib11 OBJECT test1/sub1/src1.c)
set_target_properties(lib11 PROPERTIES COMPILE_FLAGS "-DF1")
add_library(lib12 OBJECT test1/sub1/src1.c)
set_target_properties(lib12 PROPERTIES COMPILE_FLAGS "-DF2")
add_library(lib1 INTERFACE)
target_sources(lib1 INTERFACE $<TARGET_OBJECTS:lib11>)
target_sources(lib1 INTERFACE $<TARGET_OBJECTS:lib12>)
add_library(lib21 OBJECT test1/sub2/src2.c)
set_target_properties(lib21 PROPERTIES COMPILE_FLAGS "-DF1")
add_library(lib22 OBJECT test1/sub2/src2.c)
set_target_properties(lib22 PROPERTIES COMPILE_FLAGS "-DF2")
add_library(lib2 INTERFACE)
target_sources(lib2 INTERFACE $<TARGET_OBJECTS:lib21>)
target_sources(lib2 INTERFACE $<TARGET_OBJECTS:lib22>)
add_library(PARENT INTERFACE)
target_link_libraries(PARENT INTERFACE lib1)
target_link_libraries(PARENT INTERFACE lib2)
add_library(CORE OBJECT src.c)
add_library(GPARENT STATIC $<TARGET_OBJECTS:CORE>)
target_link_libraries(GPARENT INTERFACE PARENT)
Several OBJECT libraries can be incorporated into one INTERFACE library:
# Create lib1 OBJECT library with some sources, compile flags and so.
add_library(lib1 OBJECT ...)
# Create lib2 OBJECT library with some sources, compile flags and so.
add_library(lib2 OBJECT ...)
# Create INTERFACE library..
add_library(libs INTERFACE)
# .. which combines OBJECT libraries
target_sources(libs INTERFACE $<TARGET_OBJECTS:lib1> $<TARGET_OBJECTS:lib2>)
Resulted library can be used with standard target_link_libraries:
add_library(mainLib <some-source>)
target_link_libraries(mainLib libs)
It is possible to combine INTERFACE libraries futher:
# Create libs_another INTERFACE library like 'libs' one
add_library(libs_another INTERFACE)
..
# Combine 'libs' and 'libs_another' together
add_library(upper_level_libs INTERFACE)
target_link_libraries(upper_level_libs INTERFACE libs libs_another)
Resulted libraries' "chain" could be of any length.
While this way for combination of OBJECT libraries looks hacky, it is actually allowed by CMake documentation:
Although object libraries may not be named directly in calls to the target_link_libraries() command, they can be "linked" indirectly by using an Interface Library whose INTERFACE_SOURCES target property is set to name $<TARGET_OBJECTS:objlib>.
I just collect objects from all places using set with PARENT_SCOPE.
root CMakeLists.txt:
set(OBJECTS)
add_subdirectory(lib1)
add_subdirectory(lib2)
add_library(lib STATIC ${OBJECTS})
CMakeLists.txt in subdirectories:
add_subdirectory(lib11)
add_library(${PROJECT_NAME} OBJECT src1.c)
list(APPEND OBJECTS $<TARGET_OBJECTS:${PROJECT_NAME}>)
set(OBJECTS ${OBJECTS} PARENT_SCOPE)

Cmake Linking Shared Library: "No such file or directory" when include a header file from library

I am learning to build a library using Cmake. The code structure for building library is like below:
include:
Test.hpp
ITest.hpp // interface
src:
Test.cpp
ITest.cpp
In CMakeLists.txt, the sentences I used to build library is :
file(GLOB SRC_LIST "src/iTest.cpp" "src/Test.cpp" "include/Test.hpp"
"include/iTest.hpp" "include/deadreckoning.hpp")
add_library(test SHARED ${SRC_LIST})
target_link_libraries( test ${OpenCV_LIBS}) // link opencv libs to libtest.so
Then I wrote another test file (main.cpp), copy and paste the library under the same directory, link library and call functions inside the library.
This CMakeLists.txt is
cmake_minimum_required(VERSION 2.8)
project(myapp)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread -O3 -Wall -ftree-vectorize -ffast-math -funroll-loops")
add_executable(myapp main.cpp)
target_link_libraries(myapp "/home/labUser/test_lib/libtest.so")
The main.cpp compiles and runs succussfully if I don't include the header files inside the library:
#include <iostream>
using namespace std;
int main(){
cout << "hello world" << endl;
return -1;
}
But when I include the header file #include "ITest.hpp", it has error:
fatal error: iTest.hpp: No such file or directory
#include "iTest.hpp"
compilation terminated.
I don't understand why it happens.I think I have already linked the library successfully because when I run main.cpp without including header file, it doesn't give any "linking" error. And I think apparently the header files are inside the library. Why I can't include it? Can anyone help me figure this out?
Big thanks!
You have a couple of issues here.
Propagating headers to users of your target:
Whilst you've added the include file to your library target, you need to let consumers of your library target know how to find the header.
As such, when your app myapp links against your library target test, you need to tell cmake to add ./include to myapp's include search path.
There is a special cmake variable, ${CMAKE_CURRENT_LIST_DIR} which resolves to the path to the directory in which the current CMakeLists.txt being processed is.
In your instance, that is the parent folder of both src and include.
./ <-- ${CMAKE_CURRENT_LIST_DIR} is this directory
+--- CMakeLists.txt
+--- src/
| +---Test.cpp
| +---ITest.cpp
+--- include/
+---Test.hpp
+---ITest.hpp
In order to tell cmake to add a path to its include search path you use target_include_directories
For this the path will then be ${CMAKE_CURRENT_LIST_DIR}/include
So the syntax you'd be looking for is:
target_include_directories(test PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
Note that this means you don't have to add "include/iTest.hpp" and "include/Test.hpp" to your SRC_LIST glob, as the compiler will be able to find them from the above target_include_directories
Linking to your test library:
Now that you've created your library and added the include directories, to actually use it in your app, you should again use target_link_libraries, but don't specify the path to the generated .so file, instead refer to the name of the library target you created, test
target_link_libraries(myapp test)
Now myapp will know how to find Test.hpp because it will get that information from the "dependency link" you've created between myapp and test
As such, assuming the following directory structure, the following CMakeLists.txt files may work
src/
+--- library/
| +--- < sources for your shared library >
+--- app/
+--- < sources for your application >
src/CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(myapp)
add_subdirectory(library)
add_subdirectory(app)
src/library/CMakeLists.txt
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}
-std=c++11
-pthread
-O3
-Wall
-ftree-vectorize
-ffast-math
-funroll-loops")
find_package(OpenCV REQUIRED)
add_library(test SHARED "src/iTest.cpp src/Test.cpp")
target_link_libraries(test ${OpenCV_LIBS}) // link opencv libs to libtest.so
target_include_directories(test PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
src/app/CMakeLists.txt
add_executable(myapp main.cpp)
target_link_libraries(myapp test)

Create a CMake object library from other object libraries (akin to relocatable output using ld)

I have the following code structure
Project->
project.cxx
CMakeLists.txt
meta->
CMakeLists.txt
first->
first.cxx
CMakeLists.txt
second->
second.cxx
CMakeLists.txt
where first/CMakeLists.txt contains:
FILE(GLOB first_sources "*.cxx")
ADD_LIBRARY(first OBJECT ${first_sources})
second/CMakeLists.txt contains:
FILE(GLOB second_sources "*.cxx")
ADD_LIBRARY(second OBJECT ${second_sources})
meta/CMakeLists.txt contains:
ADD_SUBDIRECTORY(first)
ADD_SUBDIRECTORY(second)
ADD_LIBRARY(meta OBJECT $<TARGET_OBJECTS:first> $<TARGET_OBJECTS:second>)
and finally, Project/CMakeLists.txt containts:
ADD_SUBDIRECTORY(meta)
FILE(GLOB Project_SOURCES "*.cxx")
ADD_LIBRARY(Project SHARED ${Project_SOURCES} $<TARGET_OBJECTS:meta>)
However,
ADD_LIBRARY(meta OBJECT $<TARGET_OBJECTS:first> $<TARGET_OBJECTS:second>)
fails given that OBJECT library expects c/cxx sources instead of object files.
What I am trying to achieve is a relocatable object file that combines both first.o and second.o into a meta.o as:
ld -r first.o second.o -o meta.o
What would be the CMake alternative for the same?