cmake Headerfile include from static library - c

I'd like to build a static library with following structure :
Foo/
|-- CMakeLists.txt
|-- file1.c
|-- some_definitions.h
|-- drivers/
|-- file2.c
This is the cmake code
add_library(myHAL STATIC)
target_sources(myHAL
PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/file1.c"
"${CMAKE_CURRENT_LIST_DIR}/drivers/file2.c"
)
target_include_directories(myHAL PUBLIC ${CMAKE_CURRENT_LIST_DIR}/)
target_include_directories(myHAL PUBLIC ${CMAKE_CURRENT_LIST_DIR}/drivers/)
file2 includes some_definitions.h . In some_definitions, there are some #defines located.
I am not able to build, the definitions are not found (e.g FLS_FEATURE_A undecleared (first use))
When I move the some_definitions.h in the drivers folder, I am able to build.
What I am doing wrong? I suppose the preprocessor does not lookup for the defines or some_definitions.h is not found.

you need to use the target_include_directory or the include_directory(on older cmake versions)
this will tell your cmake to add the directory to the include path (just like -I in regular make)
this actually was discussed in a previous thread: How to properly add include directories with CMake
just include_directories("${CMAKE_CURRENT_LIST_DIR}")

Related

How can I "link" a CMake object library to another CMake object library?

I have a main -> that calls a function from object2 -> that calls a function from object1. Both object2 and object1 are CMake object libraries. Objects are passed along the chain using $<TARGET_OBJECTS:xxx and usage requirements using target_link_libraries.
Project structure:
project
|-- main
| |-- main.c
|-- object2
| |-- object2.h
| |-- object2.c
|-- object1
| |-- object1.h
| |-- object1.c
|-- CMakeLists.txt
Contents of
// CMakeLists.txt
project(objlibs)
# Object library 1
add_library(ObjectLibrary1 OBJECT object1/object1.c)
target_include_directories(ObjectLibrary1 INTERFACE object1)
# Object library 2
add_library(ObjectLibrary2 OBJECT object2/object2.c $<TARGET_OBJECTS:ObjectLibrary1>)
target_include_directories(ObjectLibrary2 INTERFACE object2)
target_link_libraries(ObjectLibrary2 PUBLIC ObjectLibrary1)
# Final executable or library
add_executable(MyTarget
main/main.c
$<TARGET_OBJECTS:ObjectLibrary2>
)
target_link_libraries(MyTarget PRIVATE ObjectLibrary2)
Trying to build, I get a linker error.
ld.exe: CMakeFiles/ObjectLibrary2.dir/object2/object2.obj:object2.c:(.text+0x18): undefined reference to `obj1func'
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
I noticed that if I modify the add_executable statement as follows (i.e. adding the $<TARGET_OBJECTS:ObjectLibrary1> line), linking goes through without issues.
add_executable(MyTarget
main/main.c
$<TARGET_OBJECTS:ObjectLibrary2>
$<TARGET_OBJECTS:ObjectLibrary1>
)
Since ObjectLibrary2 requires a symbol (the very obj1func) from ObjectLibrary1, I would expect that $<TARGET_OBJECTS:ObjectLibrary1> (as shown in my working try) would be redundant.
CMake version: 3.25.2
// object1.h
#ifndef OBJECT1_H
#define OBJECT1_H
void obj1func(void);
#endif // OBJECT1_H
// object1.c
#include "object1.h"
#include <stdio.h>
void obj1func(void){
printf("obj1func\n");
}
// object2.h
#ifndef OBJECT2_H
#define OBJECT2_H
void obj2func(void);
#endif // OBJECT2_H
// object2.c
#include "object2.h"
#include <stdio.h>
#include "object1.h"
void obj2func(void){
printf("obj2func\n");
obj1func();
}
// main.c
#include <stdio.h>
#include "object2.h"
int main(){
printf("Hello world\n");
obj2func();
}
I have tried the above with all combinations:
WSL (Ubuntu 20.04.5 LTS), Ninja (1.10.0), clang (10.0.0)
WSL (Ubuntu 20.04.5 LTS), makefiles (4.2.1), gcc (9.4.0)
Windows (10 22H2 19045.2486), Visual Studio 16, MSVC (19.29.30146.0)
Windows (10 22H2 19045.2486), Ninja (1.11.0), clang (15.0.0)
Windows (10 22H2 19045.2486), makefiles (4.3 for Win32), gcc (12.2.0)
The sequence of cmake commands is as follows. Out of source, so that I can quickly rm -rf the build tree/artifacts; building in source makes no difference.
cmake -B build/main -S .
cmake --build build/main --config Debug --verbose --clean-first
In all cases, the issue persists.
EDIT: Illustrated my answer. Please note that they might be inaccuracies here, as it's been some time since I've studied this.
I've added this graph to better illustrate what I tried to explain in the comments/here. It shows a very simplified and probably not as accurate demonstration of what happens when you link a static library (which is an archive of object files with a symbol index). Note that index is probably not the correct term, but rather symbol table (suggest an edit if you find inaccuracies please).
┌─────┐ linking ┌────────┐
│lib.a├─┬─────────►│some.elf│
└─────┘ │ ▲ └────────┘
│u │
│n │
│p │p u
│a │a p
│c │c d
│k │k a
▼ │ t
┌─────┐ │i e
│index│ │n
└─────┴┐ │t i
│l │o n
│o │ d
│o │ e
│k │ x
│u │
│p │
┌───┐ ┌─▼─┐ │
│a.o│ │b.o├─┘
└───┘ └───┘
If you wish to "link" object files, then the equivalent isn't linking but rather packing them into their "final resting place" and updating the symbol table so that the binary knows where this symbol is located. Which in your case is the executable file.
Hence why adding the Object1Library into the executable doesn't yield any errors.
Linking object files doens't make any sense, beacuse object files are missing this symbol table, they are just symbols. So if you "just link" then you aren't updating anything in the object file.
What you want to do is outlined below:
Either add_library and pack the object files into an archive with a symbol table or
add them to the executable via add_executable which adds them to the executable and updates the symbol table there or
just create a new object file altogether.
To expand on my comments I will start by posting a link to a different SO question explaining the difference between object files and libraries. There is a huge misconception between what is what here, especially because CMake coined the term "Object libraries".
In short: think of objects as a collection of symbols. While a library is not just a collection of symbols but also has (let's call it) some form of a map to these symbols.
Linking an object file to an object file doesn't make sense if you think about it that way. It makes sense to create some archive with this map that tells the linker where to look for individual symbols.
Your sample will break and give the error if I remove the Object1Library object files from the add_executable.
To fix this we can create this "dummy archive" like so:
add_library(dummy_archive STATIC) #Note that I'm not adding any files!
target_link_libraries(dummy_archive ObjectLibrary2 ObjectLibrary1)
add_executable(MyTarget main/main.c)
target_link_libraries(MyTarget PRIVATE dummy_archive)
Try to add this snippet and you will see it finally works. This is "how you link object libraries together".

How to create a library with CMakeLists.txt and include it in another program?

I'm new to C/C++ development and I'm struggling with CMakeLists.txt in CLion IDE.
I can't create a library (static or shared) that I can #include in another program.
I have this kind of structure :
src/
|
|---> utilities/
| |---> CMakeLists.txt
| |---> utilties.h
| |
| |---> file_utilities/
| | |---> file_utilities.h
| | |---> file_utilities.c
| ...
|
|---> my_app
|---> CMakeLists.txt
|---> main.c
I have generated a libutilities.so and a libutilities.a. In my main.c file, it works if I do a #include "../utilities/utilities.h" or #include "../utilities/file_utilities/file_utilities.h".
Worse, if I create another project, with another program and link my libutilities.so, the link seems to work, but still can't include my headers.
I've tried many configuration, many options in CMakeLists.txt but it has never worked until now.
Here is the CMakeLists.txt that I use for my library. I'm quite sure the problem is in this file.
cmake_minimum_required(VERSION 3.7)
project(utilities VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 11)
link_libraries(pthread)
link_libraries(ssl)
link_libraries(crypto)
find_package(CURL REQUIRED)
include_directories(${CURL_INCLUDE_DIRS})
file(GLOB_RECURSE SOURCES RELATIVES "*.c")
file(GLOB_RECURSE HEADERS RELATIVES "*.h")
add_library(${PROJECT_NAME} SHARED ${SOURCES} ${HEADERS})
set_target_properties(${PROJECT_NAME} PROPERTIES
PUBLIC_HEADER "${HEADERS}"
ARCHIVE_OUTPUT_DIRECTORY "lib"
LIBRARY_OUTPUT_DIRECTORY "lib"
OUTPUT_NAME ${PROJECT_NAME})
target_include_directories(${PROJECT_NAME} PUBLIC .)
target_link_libraries(${PROJECT_NAME} ${CURL_LIBRARIES})
include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
I'd like to have something more like #include <utilities/utilties.h> as it's a library. But I can't make it works.
In the example I provided, the library is a simple "utils" library, but I have to create more that I'll have to deliver and be integrated in another program than mine.
I highly suggest reading this article, in which a fictional library target is created, starting from project structure to installing/exporting. From my understanding, the author has exactly the same goal as you do. The article has cleared up a lot of things for me when I started out. I also recomend the project structure laid out within.
Project structure:
libjsonutils
├── CMakeLists.txt
├── include
│ └── jsonutils
│ └── json_utils.h
├── src
│ ├── file_utils.h
│ └── json_utils.cpp
└── test
├── CMakeLists.txt
└── src
└── test_main.cpp
Specifically, you will want to take a look at Targets and Properties, where generator expressions are used to differentiate between the include dir at build time, and when installed:
target_include_directories(JSONUtils
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
The install process and exporting/importing of targets is explained a little later in This is how you do CMake. What the author wants to do is be able to use the library in other cmake projects (e.g. a target called "example"), simply by doing the following:
find_package(JSONUtils 1.0 REQUIRED)
target_link_libraries(example JSONUtils::JSONUtils)
In order to make this work, you will need to install and export the library. The author encourages this for installation:
include(GNUInstallDirs)
install(TARGETS JSONUtils
EXPORT jsonutils-export
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
And this for exporting:
install(EXPORT jsonutils-targets
FILE
JSONUtilsTargets.cmake
NAMESPACE
JSONUtils::
DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/JSONUtils
)
The export will place a file named findJsonUtils.cmake in the given destination. This file will be what you need to import the library in other projects by using the find_package mechanism.
All code samples in this post taken from Pablo Arias' linked article.
Your CMakeLists.txt is inside utilities try to use this.
src/utilities/CMakeLists.txt:
target_include_directories(${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>
)
For the app you should have something like:
src/my_app/CMakeLists.txt:
target_link_libraries(${PROJECT_NAME} PRIVATE utilities)
and you should have simple
src/CMakeLists.txt:
add_subdirectory(utilities)
add_subdirectory(my_app)

Create a static library in C using CMAKE

The structure of my project is like this:
DriverFolder
Src
"myDriver.c"
Inc
"myDriver.h"
ApplicationFolder
Src
"myApplication.c"
Inc
"myApplication.h"
The "myApplication.c" source file contains this code:
#include "myApplication.h"
#include "myDriver.h" // Driver included from DriverFolder
uint8_t myFunction(void)
{
uint8_t fooValue = getValueFromDriver(); // A function in myDriver.h
return fooValue;
}
The header file "myApplication.h" contains macros and prototypes
#ifndef LIBRARY_MYAPPLICATION_H
#define LIBRARY_MYAPPLICATION_H
uint8_t myFunction(void);
#endif
I want to create a static library (.lib) using CMAKE. My goal is to include this library in another project. The other project looks like this:
#include "myApplication.h"
int main(void)
{
printf("%d", myFunction());
return 0;
}
I would like to know how to prepare the CMakeLists.txt. For now, this is what I have:
cmake_minimum_required(VERSION 3.0.0)
project(MYAPPLICATION VERSION 0.1.0)
add_library(MYAPPLICATION STATIC myDriver.c myApplication.c)
How to add include paths (DriverFolder/Inc and ApplicationFolder/Inc ?
What else should I add/modify in CMakeLists.txt in order to generate the library?
A static library is really nothing more than an archive of object files. As such they don't contain header files in themselves. Instead when you distribute or install the library, you need to also distribute or install the public header files. What you don't need is to list the header files when creating the actual library, so you don't need to add them at all.
Also, if the object files created from the listed source files (myDriver.c and myApplication.c) are all you need for the actual static library, then the shown CMakeLists.txt file is all that's needed to generate the library.
As mentioned in a comment, there are actually a couple of things missing to help build the object files needed for the library. The creation of the static library itself from the object files is fine.
The problem is that your header-files are in a non-standard location and that the compilers pre-processor will not be able to find the ones not in the same directory as the source file.
That means you have to tell CMake to add flags that tells the compiler where the header files are located. You can do this with the target_include_directories command.
And you also need to list the full (relative) path to the source file.
So the CMakeLists.txt file should probably look something like this:
cmake_minimum_required(VERSION 3.0.0)
project(MYAPPLICATION VERSION 0.1.0)
add_library(MYAPPLICATION STATIC DriverFolder/Src/myDriver.c ApplicationFolder/Src/myApplication.c)
# Tell CMake where the header files are located
target_include_directories(MYAPPLICATION PUBLIC DriverFolder/Inc ApplicationFolder/Inc)
Note that this only makes it possible to build the source files into object files. It will not include the header files inside the library, or automatically install them anywhere.

Integrating another program into existing program compiled with CMake

I'm trying to integrate a side program (Program B) into an existing program (Program A) compiled/built with CMake. Currently CMake handles finding all the resources for and compiling Program A. I would like to include a couple .h files that Program B points to, so I can embed some of Program B's .c code in Program A.
I've tried playing around with one of the CMakeLists.txt files and even added some .c files to the add_library() block, but I still receive an "undefined reference to THIS_FUNCTION" error on compile.
Thank you for your time!
EDIT: Here's part of the CMakeList.txt file I updated on the RPi
add_library(rtlsdr_shared SHARED
librtlsdr.c
tuner_e4k.c
tuner_fc0012.c
tuner_fc0013.c
tuner_fc2580.c
tuner_r82xx.c
gpu_fft.c #Added this and a couple other .c files
)
target_link_libraries(rtlsdr_shared
${LIBUSB_LIBRARIES}
)
EDIT 2:
Proj A => rtl-sdr
Proj B => gpu_fft
rtl-sdr/
CMakeLists.txt
build/
cmake/
include/
m4/
src/
CMakeLists.txt
gpu_fft/
makefile
I think you better should build program B as a shared library, and add to the include_directories of program A the includes of program B. Then target_link_libraries to your program A.
Edit:
What I have in mind is a project with this folder structure:
projA/
CMakeLists.txt
include/
src/
B/
CMakeLists.txt
include/
src/
The CMakeLists.txt in B is very classical and build the shared library libB.so (e.g. on linux, .dll on WIN32).
In the CMakeLists.txt of projA folder put:
# first build project B
add_subdirectory(B)
# add include directory of project B
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/B/include")
# [...] build here your project
# and finally:
target_link_libraries(A B)
I hope it helps
EDIT2:
To build shared libraries you can set:
OPTION(BUILD_SHARED_LIBS TRUE)
or simply
SET(BUILD_SHARED_LIBS TRUE)
EDIT 3:
You can also use it simply as an external library. Set libraries search path first:
LINK_DIRECTORIES(${yourPathToLibB})
Don't forget to
include_directories("${yourPathToLibB}/include")
too. And then just do
TARGET_LINK_LIBRARIES(A B)

CMAKE - How to properly copy static library's header file into /usr/include?

I'm getting into CMAKE usage with C and actually I'm creating two very small static libraries.
My goal is:
The libraries are compiled and linked into *.a files. [THIS
WORKS]
Then I wish to copy that *.a files into /usr/local/lib [THIS ALSO WORKS]
As far as I know about libraries (very little), they are linked using -lnameoflib, which is a compiler flag. OK. I have prepared my CMakeLists.txt and it actually copies *.a files into /usr/local/lib. However, to be able to use them in a program, I also need to copy their header files into /usr/local/include, then I can include them the easy way #include <mylibheader.h>. That's how I understand it now.
And my question is - how is the proper way of copying header files into /usr/include folder with CMAKE? I would like it to copy them automatically when make install is executed, like *.a files are.
For both of the libraries I have a smiliar CMakeLists.txt:
project(programming-network)
add_library(programming-network STATIC
send_string.c
recv_line.c
)
INSTALL(TARGETS programming-network
DESTINATION "lib"
)
A better way for newest cmake version is to use target's PUBLIC_HEADER properties.
project(myproject)
add_library(mylib some.c another.c)
set_target_properties(mylib PROPERTIES PUBLIC_HEADER "some.h;another.h")
INSTALL(TARGETS mylib
LIBRARY DESTINATION some/libpath
PUBLIC_HEADER DESTINATION some/includepath
)
Some ref:
PUBLIC_HEADER
CMake install command
In a much better way, will copy all files that match the pattern and will preserve the directory structure.
INSTALL (
DIRECTORY ${CMAKE_SOURCE_DIR}/include/
DESTINATION include
FILES_MATCHING PATTERN "*.h*")
Years later, with CMake 3.23, we can use FILE_SET for public headers:
project(programming-network)
add_library(programming-network STATIC)
target_include_directories(programming-network PRIVATE "${PROJECT_SOURCE_DIR}")
target_sources(programming-network
PRIVATE send_string.c recv_line.c
PUBLIC FILE_SET HEADERS
BASE_DIRS ${PROJECT_SOURCE_DIR}
FILES publicheader1.h publicheader2.h)
install(TARGETS programming-network FILE_SET HEADERS)
Now let's see what these commands do:
add_library(): defines the name of the target, STATIC for a static library, SHARED for a shared library, OBJECT for objects.
target_include_directories(): this line here is only for when you have subdirectories and private headers referencing each other relative to the project directory. But generally, this command is used for including external headers in a project.
target_sources(): This command is used to add definition files and private headers with PRIVATE keyword. Also, it is used to add public headers via FILE_SET keyword. BASE_DIRS is to turn the absolute path of public headers into a relative path by deducting the base directory from their path. So the this public header
/home/someuser/programming-network/sub1/publicheader1.h
with base dir of
/home/someuser/programming-network/
will be installed in
/cmake/install/prefix/include/sub1/publicheader.h
Note target_sources() can be used in CMakeLists.txt of subdirectories as well.
install(): is to install binaries, static/shared libraries and public headers. The default installation subdirectories are bin, lib and include. You can also change that like this
install(TARGETS myTarget
# for executables and dll on Win
RUNTIME DESTINATION bin
# shared libraries
LIBRARY DESTINATION lib
# for static libraries
ARCHIVE DESTINATION lib
# public headers
INCLUDES DESTINATION include)
And finally, the project is built and installed with (for multi-configuration generators: MS Visual C++, Xcode)
# in project directory
mkdir build
cd build
cmake ..
cmake --build . --config Release
cmake --install . --prefix "/usr/local/" --config Release
For single-configuration generators (make, Ninja), drop the above --config Release terms and change cmake .. to cmake -DCMAKE_BUILD_TYPE=Release ...
I don't think your solution is the correct one. /usr/include should be reserved for your vendor to put files in.
The proper thing to do IMO is to install the header in /usr/local/include and then instruct the user to export CPATH="/usr/local/include:${CPATH}".
It seems /usr/local/lib was search automatically but if you wish to use another dir export LIBRARY_PATH="/usr/local/lib:${LIBRARY_PATH}" works similar for the .a binary (but may or may not work good for shared libraries depending on your os).
Optionally, but more cumbersome is to add -I /usr/local/include and -L /usr/local/lib while compiling.
This is a somewhat subjective answer, but it's been working well for me.
In addition to the accepted answer, if you are creating a lot of libraries and the set_property syntax throws you off. You could wrap it in a very simple macro, such as:
# File: target_public_headers.cmake
macro(target_public_headers TARGET)
set_target_properties(${TARGET} PROPERTIES PUBLIC_HEADER "${ARGN}")
endmacro()
Then you can use it like:
project(myproject)
include(target_public_headers)
add_library(mylib some.c another.c)
target_public_headers(mylib some.h another.h) # <<<<<
# If you're exporting this library then you need to tell
# CMake how to include the "installed" version of the headers.
target_include_directories(mylib
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
PUBLIC $<INSTALL_INTERFACE:some/includepath>
)
INSTALL(TARGETS mylib
LIBRARY DESTINATION some/libpath
PUBLIC_HEADER DESTINATION some/includepath
)

Resources