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

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.

Related

What's the difference between passing a "xxx.a" file and a target to target_link_libraries()?

When I pass a target to target_link_libraries(), it worked fine.
But when I pass a .a file (the "worked fine target" produced), it worked fine while compiling but produced many errors while linking.
I have a project, and the CMakeFiles.txt is like this:
file (sources a.cc b.cc ...)
add_executable(my_project ${sources})
Now I want to produce a static library using the same source files to support another project.
So the CMakeFiles.txt is like this:
file (sources a.cc b.cc ...)
add_library(my_project ${sources})
add_executable(another_project main.cc)
target_link_libraries(another_project my_project)
It works fine, But when I try to link .a file directly like this
target_link_libraries(another_project libmy_project.a)
And it produced lots of "undefined reference to" or "multiple defination to" error when linking the target.
So what exactly happen when pass a target to "target_link_libraries"?
First, if you link the target against a file, you should first ensure this file exists before linking happens, thus you should add target dependencies between the executable and library targets (provided both are part of the same CMake project)
add_dependencies(another_project my_project)
Second, if you link against an arbitrary file (which is not any of the system libraries), you are supposed either provide full path to the file:
target_link_libraries(another_project ${CMAKE_CURRENT_BINARY_DIR}/libmy_project.a)
Or add the folder where the library is located to the current library search directories list:
link_directories(${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(another_project libmy_project.a)
So what exactly happen when pass a target to "target_link_libraries"?
Simply put, when CMake hits a reference to a library in a form of libName.a, it just adds -lName flag and lets the linker locate the library itself. The linker then fails, because it looks for corresponding archives under LIBRARY_PATH paths only by default.

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.

Why CMake's add_executable requires at least one source file? [duplicate]

I would like to build an executable from static libraries (i. e. .a-files) only. This is possible, because the main() function is contained in one of these libraries.
The add_executable() function requires me to provide at least one source file. But this is not what I want to do.
There is no way to do it without a hack. You need at least one *.c or *.cpp file.
What I do is make a dummy null.cpp file (zero bytes) and use that. You can also use /dev/null but that only works on Linux.
file(WRITE null.cpp "")
add_executable(tester
null.cpp
)
target_link_libraries(tester
-Wl,--whole-archive
libtest1
libtest2
libtest3
libtest4
-Wl,--no-whole-archive
gtest_main
)
There are mainly two reasons why a source file is enforced by CMake:
To determine the LINKER_LANGUAGE from the file ending(s)
Not all compilers do support an object/library only link step (for details see below)
And if you move the main() function to library please keep the following in mind: Why does the order in which libraries are linked sometimes cause errors in GCC?
So if you build the libraries with CMake in the same project, I would recommend to change your libraries (at least the one containing your main() function) to an object library:
cmake_minimum_required(VERSION 2.8.8)
project(NoSourceForExe)
file(WRITE main.cc "int main() { return 0; }")
add_library(MyLibrary OBJECT main.cc)
add_executable(MyExecutable $<TARGET_OBJECTS:MyLibrary>)
The add_library() documentation lists a warning here:
Some native build systems may not like targets that have only object files, so consider adding at least one real source file to any target that references $<TARGET_OBJECTS:objlib>.
But those are rare and listed in Tests/ObjectLibrary/CMakeLists.txt:
# VS 6 and 7 generators do not add objects as sources so we need a
# dummy object to convince the IDE to build the targets below.
...
# Xcode does not seem to support targets without sources.
Not knowing which host OS(s) you are targeting, you may just give it a try.
References
CMake Object Lib containing main
CMake/Tutorials/Object Library

Cmake: link openscenegraph to my shared library

I'm new in using CMake, and I need to link openscenegraph library to my shared library.
Part of code responsible for it looks like this:
add_library (MyLib SHARED ${${PROJECT_NAME}_sources})
target_link_libraries(MyLib ${OPENSCENEGRAPH_LIBRARIES})
Im finding osg like this:
find_package(OpenSceneGraph REQUIRED osgDB osgUtil osg osgViewer osgGA osgShadow)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
add_definitions(${OPENSCENEGRAPH_DEFINITIONS})
And everything looks like it is linking, CMake isn't giving me any errors, but during building with Visual Studio 2010 I got errors like:
fatal error C1083: Cannot open include file: 'osg/Node': No such file or directory
Usually I was finding answers to all my questions without asking them (this is my first question here).
This may be trivial, but can someone tell me what am I doing wrong, and how can I make it work?
This doesn't seems related to linking, but to compile. The error says that MSVC doesn't find the file 'osg/Node' in the list of included directories.
Somewhere in your source code, you probably have an include like
#include "osg/Node"
so compiler will search for a file called 'Node' in the directory called 'osg' in one of the 'include directories'.
In CMake, print the list of include dirs for OpenSceneGraph (after find_package(OpenSceneGraph ...)
message(STATUS "OSG Incl dirs: ${OPENSCENEGRAPH_INCLUDE_DIRS}")
then check this list. If it contains something like C:/path/to/openscenegraph/include then you should import Node using
#include "osg/Node"
If instead you have a folder for each module (ex: C:/path/to/openscenegraph/include/osg) so you must use
#include "Node"
If you don't want to modify includes in your code, then you have to add C:/path/to/openscenegraph/include in your include directories (from CMake script)
include_directories(C:/path/to/openscenegraph/include)

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

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} )