Link two dependent projects with CMake - cmake

Let I have two projects A & B. A has a structure: src, tests, vendor(3d party libs). I want to put the project B with the same structure into A's src. A and B are dependent (A uses files from B; B uses files from A and from A's vendor). I want to be able to run tests for A and B separately. Is there a way to do it with CMake?

As mentioned by ComicSansMS in the comments, you want to split this into three projects: A, B and C. Thew new project C contains the stuff that is used by both A and B. Without knowing more specifics, it's hard to suggest the particular action you should take to split up the projects.
As far as CMake building the resulting three-part project, yes, it's fairly simple:
add_library(C c.cpp c2.cpp)
add_library(A a.cpp a1.cpp)
target_link_libraries(A C)
add_library(B b.cpp b2.cpp)
target_link_libraries(B C)
add_executable(test test.cpp)
target_link_libraries(test A B)

Related

Why the use of generator expression to link object library?

I understand the purpose of object libraries, but why does linking it to a target need to involve a generator expression?
add_library(myObjects OBJECT a.cpp b.cpp)
add_library(mainLib ${other_srcs} $<TARGET_OBJECTS:myObjects>) # Why not "PRIVATE myObjects" just like other files and targets?
From my understanding, generator expressions are useful for evaluating things available only during generation phase. Why would it be the case that object libraries, which are simply collection of object files, and their location not be known during the configuration phase?
In addition to what #Alex Reinking has already said, the location of the object file may itself depend on the configuration, e.g. with the "Ninja Multi-Config" generator the object files of a target t are located in <build-dir>/t/CMakeFiles/t.dir/<config>/<path-of-source-file-relative-to-dir-in-which-target-is-defined>/<src-file-name>.o
So you're exactly in the situation that you only know the location at generation time, hence the need for a generator expression.
add_library(mainLib ${other_srcs} $<TARGET_OBJECTS:myObjects>) # Why not "myObjects" just like other files and targets?
So:
add_library(mainLib ${other_srcs} myObjects)
Because that would try to find a file named myObjects and compile it. The file has no extensions, so cmake will exit because the file does not exists and he doesn't know how to compile it anyway.
If you meant:
add_library(mainLib ${other_srcs})
target_link_libraries(mainLib PUBLIC myObjects)
The link_libraries dependency on object libraries did not work in older cmake when OBJECT libraries were introduced.
The documentation on object libraries explicitly mentions the form add_library(... $<TARGET_OBJECTS:...) and notes that since cmkae 3.12 target_link_libraries may be used with object libraries.
Why would it be the case that object libraries, which are simply collection of object files, and their location not be known during the configuration phase?
Simple: the target_sources command may be used to add sources (and therefore objects) to a target at any point. If a variable were expanded immediately, one would miss objects that were added later.
In multi-config builds, it is also unknown at configuration time whether you refer to the Debug or Release objects.
Imagine that add_library(target a.cpp b.cpp) created a variable named target_OBJECTS containing /path/to/a.o;/path/to/b.o. Then the following sequence demonstrates the problem:
add_library(target a.cpp b.cpp)
add_executable(main main.cpp ${target_OBJECTS}) # (1)
# ... later ...
target_sources(target PRIVATE c.cpp)
# ... later ...
add_executable(utility util.cpp ${target_OBJECTS}) # (2)
Now how would (2) know about /path/to/c.o? You could have target_sources update that variable, but now a single object library has dependents that see different subsets of it. That's horribly confusing. Moreover, (1) wouldn't see c.o, which is maybe what's desired in this example, but that's not always true. Maybe your code is just organized to define custom commands to create generated sources later.
Making this imperative, rather than declarative like $<TARGET_OBJECTS:target>, imposes structure on CMake code akin to requiring targets in a Makefile to be topologically sorted. It also changes the meaning from "the object files for this target" to "the object files for this target at this point in CMake execution time", which is strictly more complicated to reason about while not offering any expressive advantage.
If you replace this code with the CMake 3.12+ object library linking support, it looks even more vexing:
add_library(target a.cpp b.cpp)
add_executable(main main.cpp)
target_link_libraries(main PRIVATE target) # (1)
# ... later ...
target_sources(target PRIVATE c.cpp)
# ... later ...
add_executable(utility util.cpp)
target_link_libraries(utility PRIVATE target) # (2)
Now, looking at either call to target_link_libraries(... target), one would reasonably expect the same objects to be added.

ILMerge fails to merge: The assembly 'xxx' was not merged in correctly

Problem
Using ILMerge I am trying to merge three assemblies: A, B and C. It fails with the following errormessage. I really need to help to solve this.
"ILMerge.Merge: The assembly 'B' was not merged in correctly. It is still listed as an external reference in the target assembly."
Assembly A does not reference the other two. B references A. C references A and B.
Command line: ilmerge /v2 /target:library /out:test.dll a.dll b.dll c.dll
What I have tried
Use /closed argument. Does not make any difference.
Different versions of ILMerge, I get the same error with both version 3.0.21.3911 and 2.14.1208.
Merge A and B first and the merge in C with the A+B merge. This does not produce any error message but the resulting file is broken. It does not run and when I run in through .NET Obfuscator it complains about it.
Some background
The project is larger than the three assemblies but I have managed to reduce the problem down to these. If I dont include assembly C and only merge A and B the problem goes away.
In the previous version I did not have this problem. Between these two versions there is only non functional changes, like renames (not assembly or namespaces, only classes, methods etc) and reordering of access modifiers (static internal->internal static)
From the documentation
A common problem is that after merging some assemblies, you get an error message
stating that an input assembly was not merged in correctly because it is still listed as an
external reference in the merged assembly. The most common cause of this problem is
that one of the input assemblies, B, has an external reference to the incorrectly merged
assembly, A, and also an external reference to another assembly, C, that itself has an
external reference to A. Suppose the reference to C is to a method that takes an argument
whose type is defined in A. Then the type signature of the method call to the method in C
still refers to the type from A even in the merged assembly. After all, ILMerge cannot go
and modify the assembly C so that it now depends on the type as it is defined in the
output assembly after it has been merged!
The solution is to use the closed option (Section 2.6) to have ILMerge compute the
transitive closure of the input assemblies to prevent this kind of “dangling reference”. In
the example, that would result in all three assemblies A, B, and C, being merged. There is
no way to merge just A and B without there still being an external reference to A.

What is the purpose of the TARGET_NAME generator expression?

In the CMake documentation describing generator expressions there is this paragraph:
$<TARGET_NAME:...>
Marks ... as being the name of a target. This is required if exporting targets to multiple dependent export sets. The ... must be a literal name of a target- it may not contain generator expressions.
I am trying to understand the highlighted part.
First I want to see if I understand correctly the notion of dependent export sets. If target B depends on target A and if export set EXP_A contains target A and export set EXP_B contains target B, then the export set EXP_B is dependent on the export set EXP_A. Is this correct?
Why is $<TARGET_NAME:...> "required if exporting targets to multiple dependent export sets"?
I'm not sure what they mean by "multiple dependent export sets". I looked at their test suite and couldn't find anything like that. It's confusing to me, too.
However, I can give you an example where $<TARGET_NAME:...> actually does something. Consider:
cmake_minimum_required(VERSION 3.20)
project(test)
add_library(define_a INTERFACE)
target_compile_definitions(define_a INTERFACE A)
add_library(define_a_tgt INTERFACE)
target_compile_definitions(define_a_tgt INTERFACE "A_TGT=\"$<TARGET_NAME:define_a>\"")
include(GNUInstallDirs)
install(TARGETS define_a define_a_tgt EXPORT test)
install(
EXPORT test
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/test
FILE test-config.cmake
NAMESPACE test::
)
Here, we have two silly INTERFACE libraries. The first, define_a, simply propagates -DA to the target linking to it. The second, define_a_tgt adds a definition of A_TGT such that it is equal to the string containing the name of the target define_a.
In the build interface, this will expand to simply define_a, but after exporting it, CMake will replace $<TARGET_NAME:define_a> with test::define_a (on account of the NAMESPACE argument to install(EXPORT)).
This is a bit contrived, but it does show an instance where CMake is not already aware that a target is being named (it transforms target_link_libraries targets automatically, without this genex). Another instance is inside other generator expressions (even in target_link_libraries).

Specifying target_link_libraries when building shared object in cmake

My project creates a shared object A that 'depends' should reference other shared objects B, C , D and E. After building the project and checking the build with the utility ldd I see no references to the shared objects B, C , D and E. However when I use the directive target_link_libraries(A , B , C , D , E ) in my build the references to the shared objects appear in A.so. My question is two fold:
Is it correct to use target_link_libraries in this way?
If so, why is this use of target_link_libraries correct considering that I'm building a shared object where linking is at runtime.
Example:
My Frobnigator project depends on a ContinuumTransfunctioner and Transmogrifier shared objects that have already been built. My question is about whether the line target_link_libraries(Frobnigator ${Libraries}) is necessary.
cmake_minimum_required(VERSION 3.0)
set(Libraries
ContinuumTransfunctioner
Transmogrifier
)
set(SourceFiles
Wrapper.cpp
Logger.cpp
)
add_library(Frobnigator SHARED ${SourceFiles})
add_library(FrobnigatorStatic STATIC ${SourceFiles})
set_target_properties(FrobnigatorStatic PROPERTIES OUTPUT_NAME Frobnigator)
#target_link_libraries(Frobnigator ${Libraries}) # Do I need this?
Yes, you need to use target_link_libraries even when create SHARED library.
While some symbol resolution is performed at runtime (loading time), there some things which should be performed at build time (linking).
The main thing is ... providing a list of libraries, which should be loading with your library. This list is "embedded" into the library file. There is no other way for dynamic loader to know, which other libraries should be loaded with your one.
Among other things performed at link time is:
Checking that all symbols needed for your library are actually available in other libraries.

How to specify library dependency introduced by header file

Suppose in a CMake project, I have a source that is built into a library
// a.cpp
void f() { /* some code*/ }
And I have a header
// b.h
void f();
struct X { void g() { f(); } };
I have another file:
// main.cpp
#include "b.h"
int main() { X x; x.g(); }
The CMakeLists.txt contains:
add_library(A a.cpp)
add_executable(main main.cpp)
target_link_libraries(main A)
Now look at the last line of the CMakeLists.txt: I need to specify A as the dependencies of main explicitly. Basically, I need to specify such dependencies for every source that includes b.h. Since the includes can be indirect and go all the way down through a chain of includes. For example, a.cpp calls a class inline function of c.h, which in turns calls function in d.h, etc, and finally calls function from library A. If b.h is included by lots files, manually finding out all such dependencies is not feasible for large projects.
So my question is, is there anyway to specify that, for every source file that directly or indirectly includes a header, it needs to link against certain library?
Thanks.
To make one thing clear: You a.cpp gets compiled into a lib "A". That means that any user of A, will need to specify target_link_libraries with A. No way around it. If you have 10 little applications using A, you will need to specify target_link_libraries ten times.
My answer deals with the second issue of your question and I believe it is the more important one:
How to get rid of a chain of includes?
By including a.h in b.h and using its method in b.h you are adding a "implicit" dependency. As you noticed, any user of b.h needs a.h as well. Broadly speaking, there are two approaches.
The good approach:
This has nothing to do with CMake, but is about encapsulation. The users of your library (incl. you yourselves) should not need to worry about its internal implementation. That means: Don't include b.h in a.h.
Instead, move the include to a .cpp file. This way, you break the chain. E.g. something like
// b.h
void f();
struct X
{
void g();
};
// b.cpp
#include b.h
#include a.h
void X::g( )
{
f();
}
This way, the use of a.h is "contained" in the cpp file and anyone using you library need only include b.h and link to b.lib.
The alternative:
Now, there are situations where you have to accept such a "dependency" or where it is a conscious choice. E.g. when you have no control over A or when you conciously decided to create a library defined in terms of classes/structs internal to A.
In that case, I suggest you write a piece of CMake code, which prepares all the necessary include-dirs down the chain. E.g. define a variable "YOURLIB_INCLUDES" and "YOURLIB_LIBRARIES" in "YourLibConfig.cmake" and document that any user of your library should import "YourLibConfig.cmake". This is the approach several cmake-based projects take. E.g. OpenCV installs a OpenCVConfig.cmake file, VTK installs a VTKConfig.cmake and prepares a UseVTK.cmake file