Using cmake's automoc on external header files without knowing their filenames - c++

In essence I want to be able to moc header files that are not part of any target in cmake with the additional difficulty that I don't know the filenames of those headers.
The actual project is quite complex and part of an automated build system. The following is an easy example.
Consider a project structured like this:
CMakeLists.txt
src/lib/source.cpp
src/lib/CMakeLists.txt
src/include/some_header.hpp # which is included in source.cpp
Content of main CMakeLists.txt:
cmake_mimimum_required(VERSION 2.8.6)
project("automoctest")
add_subdirectory(src/lib)
Content of src/lib/CMakeLists.txt:
include_directories(${CMAKE_HOME_DIRECTORY}/src/include)
find_package(Qt4 4.8 REQUIRED QtCore)
include(UseQt4)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_library(foo SHARED source.cpp)
target_link_libraries(foo ${QT_LIBRARIES})
set_target_properties(foo PROPERTIES AUTOMOC true)
Inside source.cpp the file some_header.hpp is included like this:
#include "some_header.hpp"
The Problem:
The file some_header.hpp includes a Q_OBJECT and has some signals, so moc needs to work its magic. But as the header is not inside the actual project the header will not get mocked. I don't even know the filename of some_header.hpp so I can't add it specifically to be mocked. Obviously AUTOMOC does not check the include_directories for mockable files even when a source file includes one of them.
What I tried (unsuccessfully):
use #include moc_some_header.cpp in source.cpp as it is described in the cmake documentation. This leads to an error in which cmake complains that it could not find some_header{.h,.hpp,.hxx,.H}
setting CMAKE_AUTOMOC_RELAXED_MODE to true. Even though it's not clear from the doc what this actually does. Made no difference anyway.
setting AUTOMOC_MOC_OPTIONS to -Isrc/include or -I/path/to/project/src/include or -I${CMAKE_HOME_DIRECTORY}/src/include Doesn't do anything that I could see.
The great thing about AUTOMOC is that I don't need to know which files actually need to be mocked. In this case however I would need to know all the headers that might have a Q_OBJECT inside, that are included by my source files.
Is there a way to tell AUTOMOC where exactly to look for mockable files?

Did you truly set AUTOMOC_MOC_OPTIONS to -Isrc/include, or to -I/path/to/myproject/src/include? The former probably doesn't exist.
I have always used the MOC macros; it looks like AUTOMOC is something new that is built into CMake.
I usually include all headers when creating a library or executable - i.e.
add_library(foo SHARED source.cpp ../include/some_header.hpp )
I assume that this will allow AUTOMOC to work. It will have the added benefit of causing make to work as expected - rebuilding your library when the header changes.
If you are truly unable to add the file to the add_library command ahead of time, then I think you'll have to write a cmake function to search for #include statements within source.cpp, locate those files, and search them for Q_OBJECT. If they match, add them to a variable - perhaps EXTRA_MOCS. Then, the add_library line becomes
add_library(foo SHARED source.cpp ${EXTRA_MOCS} )

Related

C++ - Why is a Static Library unusable without source files?

So from what I understand, a static compiled mylib.a file should just be usable as plug-and-play. But I can't use it without the source code, I'm trying to avoid that.
I adapted this tutorial to compile using CMake with the following directory structure:
/
lib
libmy_math.a
main.cpp
my_math.h
my_math.cpp
CMakeLists.txt
The contents of the files are exactly as in the tutorial. The only added file is CMakeLists.txt which basically just runs the library compiling part before building:
CMakeLists.txt
cmake_minimum_required(VERSION 3.21)
project(Test)
### Uncomment this ONCE to compile a libmy_math.a file
# add_library(my_math STATIC my_math.cpp)
add_executable(Test main.cpp)
find_library(MY_MATH_LIB my_math HINTS ./lib)
target_link_libraries(Test PUBLIC ${MY_MATH_LIB})
As the CMake file says, uncommenting Line 5 compiles the library, which is then linked to my code when compiling.
I'm trying to package my code so that I don't show my client the source code for the compiled library (my_math), but if I delete the my_math.h and my_math.cpp files, I get a "file not found" error on import:
/Users/Joe/Desktop/Test/main.cpp:1:10: fatal error: 'my_math.h' file not found
#include "my_math.h"
^~~~~~~~~~~
I thought you could compile libraries without needing the source code. What am I missing here?
A static library does not contain each and every definition a header can contain - think of macros, etc. Thus you still need the header. However, you don't need the .cpp anymore to link the library.
Besides the problem stated in Iorro's answer the find_library call here will not work as expected.
find_library(MY_MATH_LIB my_math HINTS ./lib)
target_link_libraries(Test PUBLIC ${MY_MATH_LIB})
What this does is, while running CMake, look for the my_math.a file. However, at that point of the build process, my_math.a has not been built yet. You're just running CMake for the first time, so all you have is source files. Nothing has been compiled yet.
What you want to do instead is link to the library target directly, as CMake already knows about that and can resolve the dependency on its own. So you need to replace the two lines above with:
target_link_libraries(Test PUBLIC my_math)
This now introduces a curious asymmetry in how your test consumes the library (as it is part of the same build) vs. how your clients consume the same library (which are not part of the same build and thus need to do a find_package call before they can use it). Look at CMake's config-file packages for a mechanism that allows you to restore symmetry here, at the cost of a significant amount of boilerplate required in your CMake script.

CMake/make apply -D flags on header files

I'm (cross-)compiling a shared C library with support for many different platforms which is handled by an hierarchy of CMakeLists files. In those files, several platform specific compiler flags are conditionally produced (with add_definitions()). I can successfully compile and link the source code leading to an appropriate .so file.
But to use the library in any project, I need to provide the right header files, too. The following install command of CMake selects the right header files to copy but does not apply the replacement of preprocessor defines/includes:
install(FILES ${headers} DESTINATION include/mylibrary)
So how can I generate/install the "post-compiled" header files?
What I thought of so far:
As add_definitions() should stack my -D's in the COMPILE_DEFINITIONS variable, maybe running a foreach loop on the copied raw headers and replace the define/include placeholders?
Using add_custom_command() to apply some logic before copying?
Edit: As pointed out by Tsyvarev, there is an answer quite near to my needs here, but unfortunately not quite it. In summary, the answer gives 2 options:
Include a special 'config' header in all of the library's headers and leverage the cmakedefine command to call configure_file() on this header. I can't use this approach because I don't want to alter the library headers.
Create a target-specific .cmake file which helps external projects in including the right headers together with all necessary -D defines. I can't use this approach either, because my external projects do not use cmake for building. Plus, I wish to create a library that is as easy to include as possible.
Any other thoughts?
Edit 2: I may have to elaborate on my statement, that the install command of CMake is not replacing defines. Take the following example:
//sampleheader.hpp
#ifndef SAMPLEHEADER_HPP_
#define SAMPLEHEADER_HPP_
#include OS_SPECIFIC_HEADER
//...
Now I have a CMakeLists.txt file that does something like this:
# ...
if (${OS} MATCHES "arm-emblinux")
add_definitions(-DOS_SPECIFIC_HEADER="emblinuxHeader.hpp")
elseif (${OS} MATCHES "linux")
add_definitions(-DOS_SPECIFIC_HEADER="linuxHeader.hpp")
endif()
# ...
Everything compiles fine, but when the install command above gets called, I have a header file in my ../include/ directory still with OS_SPECIFIC_HEADER placeholder in it. And of course, this cannot be properly included in any development project.

In CLion, header only library: file "does not belong to any project target, code insight features might not work properly"

I have a header-only library project set up with the cmake command:
add_library(my_library INTERFACE)
and I also added
target_sources(my_library INTERFACE ${MY_LIRBARY_HEADER_FILES})
but when I open a source file, I get the warning:
This file does not belong to any project target, code insight features might not work properly
and I lose a lot of the functionality on things like code completion.
What is the proper way to set this up so CLion provides its usual functionality on a header-only library?
Little background
I was having the same problem, albeit the project was not header-only, nevertheless, the open files from inc folder were throwing the aforementioned warning, even though the CMake file clearly marked that folder to be include_directory.
*.hpp files do not belong to ${SOURCE}
include_directories("${PROJECT_SOURCE_DIR}/inc/")
add_subdirectory(src)
add_executable(${EXECUTABLE_NAME} main.cpp ${SOURCE})
Since this is a perfectly valid CMake file and adding the include files to source files is not idiomatic, I did not want to amend the CMake file.
The solution
As described on the official JetBrains Forum, the CMake file is indeed valid and the warning is shown because of the inability of CLion to properly index header files. The suggested workaround extracted from the link is to right-click the folder and Mark directory as | Library Files/Project Sources and Headers.
So, this header isn't includes in executables and CLion notifies you that some code insight features might not work properly. As workaround you can use "Mark directory as" Library Files/Project Source and Headers for folder.
Clion takes information about source files from CMake build system. When you add any cpp file to sources list CMake automatically tell about header with same name. So if cpp/h names differs (or you don't have cpp file at all) you should include header manually.
set(Sources my_lib.cpp)
set(Headers header_of_my_lib.h)
add_executable(superlib ${Sources} ${Headers})
If you don't have any executable you can omit last line, CLion will still know about files
This warning is an IDE issue that Android Studio cannot recognise the current directory if it does not include any source files.
Workaround
Adding am empty source file, e.g empty_xxx.c under the directory in question and adding below line in your corresponding CMakeList.txt
add_library(${TARGET_NAME_XXX} SHARED ${SOME_DIR_HAVING_THIS_WARNING}/empty_xxx.c)
will help get rid of this warning.
You can add the header files to your project like this:
set(SOURCE_FILES main.cpp MyClass1.cpp MyClass1.h MyClass2.cpp MyClass2.h)
You can also set it in multiple steps like so:
set(SOURCE_FILES main.cpp)
set(SOURCE_FILES ${SOURCE_FILES} MyClass1.cpp MyClass1.h)
set(SOURCE_FILES ${SOURCE_FILES} MyClass2.cpp MyClass2.h)
Though as mentioned in the comments, you probably shouldn't be adding the header files to your project at all.

CMake how to include headers without sources?

This is probably a dummy question but I have literally looked at the two first pages of google without success.
I'm writing a header only library and I'm unable to set up correctly the CMake configuration in order that when I build my solution a given main.cpp finds the proper includes.
How can this be accomplished?
EDIT
So I probably should give a little more detailed explanation.
Lets say I have a ./src folder with: ./src/core and ./src/wrappers. Inside each folder I have .h files that needs to be included in a main.cpp file:
#include <src/core/reader.h>
Still when I put in CMakeList.txt something like:
include_directories(src/core)
add_executable(main main.cpp)
I receive a message like: src/core/reader.h no such file or directory.
To be able to use that path, you should refer to the parent directory of src.
Assuming the top level CMakeLists.txt is at the same level of src, you can use this instead:
include_directories(${CMAKE_SOURCE_DIR})
As from the documentation of CMAKE_SOURCE_DIR:
The path to the top level of the source tree.
If src is directly in the top level directory, this should let you use something like:
#include <src/whatever/you/want.h>
That said, a couple of suggestions:
I would rather add this:
include_directories(${CMAKE_SOURCE_DIR}/src)
And use this:
#include <whatever/you/want.h>
No longer src in your paths and restricted search area.
I would probably use target_include_directories instead of include_directories and specify the target to be used for that rules:
target_include_directories(main ${CMAKE_SOURCE_DIR}/src)
This must be put after the add_executable, otherwise the target is not visible.
Another option which might make sense in some situations would be to create a dedicated target for the header-only library:
add_library(headerlib INTERFACE)
target_include_directories(headerlib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
And then to use it:
target_link_libraries(mytarget headerlib)
This has the advantage that if you want to use it in multiple targets, it's easy to do so.
Just add a include_directories() directive in order to find where your header only library can be found by the target project.
According to your edit. To find
#include <src/core/reader.h>
You need to add
include_directories(/full_parent_path_of_src)
If I understand your question correctly then in your CMakeLists.txt you need to add include_directories(<DIRECTORY>) for every directory of your header library.

Add include directories to AUTOMOC

I have a ROS package that includes QT4 GUIs. My code is in the folder
Project_name/src/test/*.cpp
and my includes in
Project_name/include/test/*.h
Some qt4 mocs must be created as some header files contain Q_OBJECT in their classes.
I tried the
set(CMAKE_AUTOMOC ON)
in the cmake file but as it seems it does not search the /include/test/ folder. AUTOMOC states that works either bu searching the source files for moc_**.cpp files or by examining the header files for Q_OBJECT.
I also tried to include a moc_***.cpp in a source file (for example /src/test/a.cpp). So it searched for a.h but could not find it in include/test/a.h.
I must note that if I remove the Q_OBJECT from the classes the compilation succeeds, as the include/ folder is added like this:
include_directories( include
${catkin_INCLUDE_DIRS}
)
Finally I've tried to use QT4_WRAP_CPP but for some reason it couldn't find the mocs as well and the link failed (although in another project with the same parameters in the cmake file works :/)
Edit :
Found a solution. In added in the cpp file:
#include "../../include/test/moc_a.cpp"
and found the .h in include/test.
Though something tells me that it is not the correct way :P
#include "../../include/test/moc_a.cpp" in cpp file works but not well for libraries that may be built sometimes as static libraries within bigger project and sometimes by themselves. The problem is that include directory can be created in a not-suitable location, which pollutes code, causes problems with VCS.
qt_wrap_cpp works best for me. It supports both qt4 and qt5, does not require including moc in cpp file. The syntax:
include_directories(${CMAKE_CURRENT_BINARY_DIR}) # including binary dir is
# necessary only if there are classes with Q_OBJECT macro declared in cpp
# files (these cpp files should also contain `# include "x.moc"` at the end).
set(CMAKE_AUTOMOC ON)
include_directories(${Include_Directories})
set(Sources ${Sources_Path}/a.cpp ${Sources_Path}/b.cpp
... ${Sources_Path_z}/z.cpp)
qt_wrap_cpp(${Target_Name} Sources ${Headers_Path}/header1.hpp
${Headers_Path_2}/header2.hpp ... ${Headers_Path_N}/headerN.hpp)
add_library(${Target_Name} STATIC ${Sources})
# OR add_executable(${Target_Name} ${Sources})
Naturally, only headers that contain Q_OBJECT macro and are not in the same directory as corresponding sources must be passed to qt_wrap_cpp.