How to link 2 files in Cmake? - cmake

I have researched this on Internet but I either don't understand the answers or the previous questions don't address my problem.
I have 3 files A,B,C, where C is the executable and A,B just some library code I write myself. B depends on A. C depends on B and A. How do I properly link them in Cmake? I tried this:
add_library(A A.c)
add_library(B B.c)
add_executable(C C.c)
target_link_libraries(B A)
target_link_libraries(C B A)
Despite that I still get the "undefined reference to xxx" error. I really don't know what I did wrong.

I have found the culprit. I accidentally wrote an include guard in my source file. So all of my functions gets deleted by the preprocessor. After deleting the guard it works normally again.

Related

Cmake library of object libraries error: install includes target "mylib" which requires target "my_object_lib" that is not in any export set [duplicate]

I try to build two libraries (say A and B) from one project. I use add_subdirectory cmake command in root cmake file. B depends on A.
When I try to add
INSTALL (TARGETS B EXPORT B
PUBLIC_HEADER DESTINATION "include/B"
LIBRARY DESTINATION "lib"
ARCHIVE DESTINATION "lib")
INSTALL (EXPORT B DESTINATION "./")
CMake warns me about error in line with INSTALL (EXPORT .... It prints:
CMake Error: INSTALL (EXPORT "B" ...) includes target "B" which requires target "A" that is not in the export set.
The error-message already tells you that you are exporting only one project, while it depends on the other project. The easiest solution is to export both projects. If they are both build by the same CMakeLists.txt you can simply call
install( TARGETS A B ... )
If not, then you probably have a top-level CMakeLists.txt (where you use add_subdirectory). You can setup an install-target there, let's call it "MyInstall". And in your subdirs refer to this top-level install-target
In your subdir...
install( TARGETS A EXPORT MyInstall ... )
similar for target B, and then you export "MyInstall" your top-level CMakeLists.txt:
install( EXPORT MyInstall ... )
Aside from fixing
"A" that is not in the export set
part of the error message, one could consider to fix the part
target "B" which requires target "A"
by using PRIVATE linkage of B with A:
target_link_libraries(B PRIVATE A)
Such linkage implies that A is needed only for building the library B, but is not needed for anyone who links with B.
It depends from the library B whether such linkage is sufficient.
But if it is, then this is a preferable way to overcome the error: If users of B doesn't need to link with A, then there is no reason to EXPORT A.
There are some easy signs when PRIVATE linkage is NOT an option:
Some public header of library B includes a header of library A.
In that case, if a user #include-s that header of B, then the header of A will be included too. And to find that header a compiler should be aware of include directories for A.
B is a STATIC library, and A is either STATIC or SHARED.
In that case, the binary file for B won't "embed" the binary file for A.
So, when link with B, one should explicitly link with A too.
However, if both A and B are SHARED libraries, then PRIVATE linkage could still be an option:
While binary file for shared B doesn't "embed" the binary file for shared A, the binary file for B contains reference to the binary file for A. So when a linker will find B binary in the command line, it will link with A binary too. If B is installed, then A should be installed too (otherwise the linker won't find A). But for being able to export B, the A needn't to be exported.

How to structure CMake projects

I started using CMake but am not sure how to use it in the following setup.
I have some library-cmake-projects (source code), which might depend on each other.
Libs
-A
-B
-C
(Lets say A depends on C and B depends on C)
How to make cmake be aware of the needed modules in the other directories?
With regards to add_subdirectory, you should structure it like your source code is structured.
So, Let's say your folder structure looks like this:
/
-- App
-- libs
-- A
-- B
-- C
Then you would put CMakeLists.txt like this:
/
-- CMakeLists.txt
-- App
-- CMakeLists.txt
-- libs
-- CMakeLists.txt
-- A
-- CMakeLsts.txt
-- B
-- CMakeLists.txt
-- C
-- CMakeLists.txt
/CMakeLists.txt
add_subdirectory(App)
add_subdirectory(libs)
/App/CMakeLists.txt
add_executable(App ...)
target_link_libraries(App A B)
/libs/CMakeLists.txt
add_subdirectory(A)
add_subdirectory(B)
add_subdirectory(C)
/libs/A/CMakeListst.txt
add_library(A ...)
target_link_libraries(A C)
/libs/B/CMakeListst.txt
add_library(B ...)
target_link_libraries(B C)
/libs/C/CMakeListst.txt
add_library(C ...)
It's not good to think about add_subdirectory as having anything to with dependency management, it is about how your source and build files are organized. It also has scoping, but most of the time you don't need to worry about it. You don't necessarily need the /libs/CMakelists.txt if you directly put all three add_subdirectory calls in the top-level CMakeLists.txt but it can help with organizing.
I would refrain from using add_subdirectory to include a neighboring folder, for example. Assume the target is there and include from somewhere higher up.
Note that this is what I would recommend in general because it seems to be what the designers had in mind, but if you need something else for some reason, or have different opinion about what is correct design, then feel free to structure it differently.
Is add_subdirectory the correct way to do this?
No, add_subdirectory() is used to traverse to other CMakeLists.txt files within your CMake project. It doesn't specify dependencies. In modern CMake, you really only need target_link_libraries to specify dependencies, as suggested in this answer.
How to add C to A to make this build in one call possible?
I do not want to add C directly to App.
You can make A depend on C by linking the previously-defined target C to A
add_library(C SHARED ... )
...
add_library(A SHARED ... )
target_link_libraries(A PUBLIC C)
Note, the use of PUBLIC here ensures C is added to the link interface of A, so it will be propagated to consuming targets. Now, when you link A to App, you will get C along with it, and there is no need to specify C explicitly.
# Link A (and C) to App.
target_link_libraries(App PRIVATE A)
How to handle the case that App also depends on B? With add_subdirectory and no caution this would try to define C twice. Are include guards the correct solution?
In my experience, this hasn't been an issue, and the linker was able to figure out the duplicated linked C library. If it does become an issue, you can simply make the scope of the B dependency PRIVATE. This way, C will not be propagated to App through B:
target_link_libraries(B PRIVATE C)
...
target_link_libraries(App PRIVATE B A)
I encourage you to read through the linked target_link_libraries() documentation, which has many examples for recommended CMake usage with these types of dependencies.

CMake: Function is callable while script, defined it, is no longer included

I have three folders in a location, say A,B and C. I have two cmake files in folder A: FindABC.cmake and UseABC.cmake. The former is for finding the libraries and the latter contains a function, say run_command(). CMakelists.txt in folder B and folder C contains the following lines:
find_package(ABC)
include(UseABC)
run_command()
It works as intended. Now If I comment find_package() and include() in CMakelists of folder C, as far as I know, Cmake should give an error telling unknown command - run_command(). But, the controls goes into the function and executes in unpredictable manner.
How come the control goes to the function when the include line is commented? The root CMakelists that lists the sub-directories does not have any find_package or include lines in it.
Edit:
UseABC.cmake:
set(ABC_COMPILE_DEBUG FALSE)
set(ABC_COMPILE_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/abc_gen")
message("USEABC1 - -> " ${ABC_COMPILE_OUTPUT_DIR})
function(run_command)
message("USEABC2 - File recurs -> " ${ABC_COMPILE_OUTPUT_DIR})
file(REMOVE_RECURSE "${ABC_COMPILE_OUTPUT_DIR}")
file(MAKE_DIRECTORY "${ABC_COMPILE_OUTPUT_DIR}")
add_custom_command() #command to be executed
endfunction()
Here, When nothing is commented(find_package and include is not commented in any CMakelists.txt), I get the correct path for the two messages I print.
When I comment include(UseABC) in the second CMakelists.txt, the configuration fails, the first message is not at all printed and the second message gets printed, but does not give the value of the variable. It also deletes all the files in Folder C (but the argument to REMOVE_RECURSE is empty).
If I correctly understand the situation, you have:
CMakeLists.txt:
add_subdirectory(B)
add_subdirectory(C)
B/CMakeLists.txt:
find_package(ABC)
include(UseABC)
In that case run_command function, defined in UseABC.cmake, is accessible in C/CMakeLists.txt, though this script doesn't define it.
In CMake function definitions are global.
By opposite, variable definitions are local to the scope, where they are defined. (Until variables are cached ones, in that case they have global visibility).
That is, variable ABC_COMPILE_DEBUG defined in UseABC.cmake is accessible in
UseABC.cmake script
B/CMakeLists.txt script, because it includes UseABC.cmake one, and include() command doesn't introduce a scope
but it is inaccessible in
CMakeLists.txt script, because add_subdirectory(B) does introduce a scope
C/CMakeLists.txt script
More details about variable's visibility can be found in documentation.

With CMake, how can I use the same source for a library for multiple different builds

I'd need to find how to best use CMake to build multiple different versions of the same library.
Let's assume that I have software A, B and C. All of these use the same external library, let's call that D. Let's assume that D is huge compared to A, B & C. So just using svn:external to checkout it multiple times is a bad option.
What crossed my mind is to have a folder where I have the subfolders for A, B, C & D and just calling add_subdir(../D). But to make things complicated, I don't want to enforce this folder structure on other developers, so I ended up with the thought of making this work with find_package, somehow.
What I'd like to see is that you are able to get C and D from subversion to any folders and have C compile it's own version of D for itself and use it, with no effort from the custom folders.
This might seem like a silly dream, but not being the smartest man on the planet, maybe someone has an idea of how to achieve this.
EDIT:
note that A B & C cannot use the same binaries for D since they would use different compiler flags to build the library.
add_subdirectory works with directories located outside the main project dir, you just have to give a binary_dir.
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
Add a subdirectory to the build. The source_dir specifies the directory in which the source CMakeLists.txt and code files are located. If it is a relative path it will be evaluated with respect to the current directory (the typical usage), but it may also be an absolute path. The binary_dir specifies the directory in which to place the output files. If it is a relative path it will be evaluated with respect to the current output directory, but it may also be an absolute path.
In A, B and C you could have something like that:
set(MYPROJECT_D_PATH "" CACHE PATH "Path to D")
if("${test}" STREQUAL "")
message(FATAL_ERROR "You must set MYPROJECT_D_PATH")
endif()
add_subdirectory(${MYPROJECT_D_PATH} D)
When you compile A, B or C for the first time, you provides the path to D like that:
cmake <src_dir> -DMYPROJECT_D_PATH=<path_to_D>

CMake: collecting libraries

I am using CMake to build a simple C++ project, hereafter named P. The structure of P is quite simple:
P/src/
P/src/package1
P/src/packege2
P/src/...
P/src/main-app
I would like to collect the libraries in package1, package2, ... in a variable called P_LIBS.
In a first attempt, I tried to collect the libraries available in package1, package2, ... in the variable called P_LIBS initially set in the src/CMakeLists.txt file. However, the updates to P_LIBS made in the CMakeLists.txt of the subfolders are not propagated to the parent folder.
I would rather not write a list of libraries in the main CMakeLists.txt file. I would rather modify such variable while moving in the directory tree.
After a search on the internet I could not find any valid suggestion. If I look at the various Find files, I only see long listings of libraries in their main CMakeLists.txt file.
Is there a way to do what (I hope) I explained above?
Thanks to sakra's link I was able to 'propagate' names up to the parent folder. However, the names I add to the P_LIBS variable are later interpreted as 'library' names, not as reference to CMake targets. In other words, if
P_LIBS = {a, b}
the 'a' and 'b' are interpreted as the library names, i.e. CMake generates:
gcc [...] -l a -o exe
instead of
gcc [...] /path/to/a.o -o exe
(.o or other extensions)
You are propably constructing the targets list as a string, try to make them a list instead. For example:
# in package1/CMakeLists.txt
set(P_LIBS ${P_LIBS} a b PARENT_SCOPE)