Transitive target_include_directories on OBJECT libraries - cmake

Here is snippet from make CMakeLists.txt:
add_library(foo-object OBJECT src/foo.cpp)
target_include_directories(foo-object PUBLIC include)
add_library(foo SHARED $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
add_library(foo_static STATIC $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
Now, this all works fine, both libraries are generated. However I have a problem when I try to use it:
add_executable(bar src/main.cpp)
target_link_libraries(bar foo)
Target bar doesn't compile, because include directories from foo-object are not propagated. If I add target_include_directories directly on foo as well, everything will compile fine.
How can I make both foo and foo_static automatically use (and forward to stuff depending on them) include directories from foo-object?

Hm, at the moment I came up with following:
add_library(foo-object OBJECT src/foo.cpp)
target_include_directories(foo-object PUBLIC include)
get_property(object_include_dirs TARGET foo-object PROPERTY INCLUDE_DIRECTORIES)
get_property(object_link_libs TARGET foo-object PROPERTY LINK_LIBRARIES)
add_library(foo SHARED $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
target_include_directories(foo PUBLIC ${object_include_dirs})
target_link_libraries(foo PUBLIC ${object_link_libs})
add_library(foo_static STATIC $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
target_include_directories(foo_static PUBLIC ${object_include_dirs})
target_link_libraries(foo_static PUBLIC ${object_link_libs})
but come on, there must be better way :/

It seems that transitive properties only work when targets are linked through a chain of target_link_library calls. In your case, you do not have such a link between foo-object and foo.
If you add a source file to foo, that one should also not be able to see the include directory from foo-object.
This might be an oversight in the design of OBJECT libraries, as it essentially breaks the transitive properties for those.

On CMake <3.12, use the following:
add_library(foo SHARED $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
target_include_directories(foo
PRIVATE
$<TARGET_PROPERTY:${PROJECT_NAME}-object,INTERFACE_INCLUDE_DIRECTORIES>)
On CMake >=3.12, take a look at this answer (thanks #ian5v for the suggestion)
How it works:
target_include_directories(...)
...
PUBLIC and INTERFACE items will populate the INTERFACE_INCLUDE_DIRECTORIES property of <target>.
Therefore ${PROJECT_NAME}-object has INTERFACE_INCLUDE_DIRECTORIES set on it. We need to fetch this property and insert it into our own include path.
This looks like a job for "generator expressions"!. In particular, $<TARGET_PROPERTY:tgt,prop> looks like it will come in handy here.
Our tgt will be ${PROJECT_NAME}-object, and we're trying to extract all of the values of INTERFACE_INCLUDE_DIRECTORIES, so INTERFACE_INCLUDE_DIRECTORIES will be prop.
This comes out to $<TARGET_PROPERTY:${PROJECT_NAME}-object,INTERFACE_INCLUDE_DIRECTORIES>, which is exactly what we've used in the code above.

For me something like the following seems to be working:
add_library(foo_objects OBJECT src/foo.cpp src/foo.hpp)
set_property(TARGET foo_objects PROPERTY POSITION_INDEPENDENT_CODE ON)
target_include_directories(foo_objects PUBLIC
"$<BUILD_INTERFACE:src>"
"$<INSTALL_INTERFACE:include>")
add_library(foo_shared SHARED)
add_library(foo_static STATIC)
target_link_libraries(foo_shared PUBLIC foo_objects)
target_link_libraries(foo_static PUBLIC foo_objects)

Related

Using exsiting target as base for a new, but related target

Say I have a cmake script with:
...
add_executable(ABC abc.cpp)
...
with some properties set, libraries linked etc.
Now I'd like to define a derived target, say ABC-extended based on ABC in the same CMakeLists.txt file, that would have all the same properties, libraries linked etc, but with a couple of new compiler flags passed say -O3 or whatever, without redefining all that.
I.e., I'd like something like this (fictional functions used):
add_derived_executable(ABC2 FROM ABC)
add_target_compile_options(ABC2 PUBLIC -O3)
You can use an INTERFACE library as base target that holds all your common flags/sources/properties (a.k.a. usage requirements). Those can then be propagated to all your derived libraries via target_link_libraries():
# base library
add_library(ABC-base)
target_compile_options(ABC-base INTERFACE ...)
target_sources(ABC-base INTERFACE ...)
# derived libraries
add_library(ABC)
target_link_libraries(ABC PRIVATE ABC-base)
add_library(ABC-extended)
target_link_libraries(ABC-extended PRIVATE ABC-base)
target_compile_options(ABC-extended PRIVATE -O3)
Note, that the base target should be "linked" PRIVATEly against the derived ones, to prevent the flags/sources/properties of the base target being passed on to other targets that link against e.g. the ABC library.

What does "populate" actually do in CMake documents?

I always confused when CMake documents say "populate the property". For example in
target_include_directories( [SYSTEM] [BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...] [<INTERFACE|PUBLIC|PRIVATE>
[items2...] ...])
...
PRIVATE and PUBLIC items will populate the INCLUDE_DIRECTORIES
property of <target>.
I'm not sure to populate the 'INCLUDE_DIRECTORIES' property with what?
For example target_include_directories(A PUBLIC B), does 'populate INCLUDE_DIRECTORIES' here means to copy B's INCLUDE_DIRECTORIES propery into A's INCLUDE_DIRECTORIES property?
PRIVATE and PUBLIC items will populate the INCLUDE_DIRECTORIES property of <target>.
It roughly does:
set(mode "PRIVATE") # or PUBLIC, from target_include_directories arguments
# Get interface include directories
target_get_proprety(incdirs B INTERFACE_INCLUDE_DIRECTORIES)
if(mode STREQUAL "PUBLIC" OR mode STREQUAL "PRIVATE")
set_properties(TARGET A APPEND PROPERTY INCLUDE_DIRECTORIES ${incdirs})
endif()
# when mode is PUBLIC or INTERFACE, they get also added to A's INTERFACE_INCLUDE_DIRECTORIES
I think the word "populate" there could be replaced with "append" without losing meaning.
I'm not sure to populate the 'INCLUDE_DIRECTORIES' property with what?
With the content of INTERFACE_INCLUDE_DIRECTORIES from the other target.
For example target_include_directories(A PUBLIC B), does 'populate INCLUDE_DIRECTORIES' here means to copy B's INCLUDE_DIRECTORIES propery into A's INCLUDE_DIRECTORIES property?
Means to append B's INTERFACE_INCLUDE_DIRECTORIES into A's INCLUDE_DIRECTORIES and into A's INTERFACE_INCLUDE_DIRECTORIES.

What is the replacement for Shared variables on Modules on VB.NET?

Does it even exist? I want to have a shared variable across a module and a class that belong to the same project.
Shared variables exist but you probably mean the old fashioned Global variables. For that, just declare a variable in an actual module rather than a Class:
Friend Foo As Integer
Public Bar As String = ""
Static vars are quite different
you can look into static variables. that's probably the closest thing to shared variables.

Doxygen: How to create extra class docs outside of source code?

I'd like to add verbose information about my core classes without embedding it all above those classes in the source file.
For instance, I'd like to create a separate file with voluminous info about class Foo, probably in markdown or html. Then when I view class Foo in the doxygen-created output, I'd like the class page for Foo to contain all my voluminous and beautifully marked up documentation in its details section along with any other comments I did put above class Foo.
Is this possible?
If you currently have a file Foo.h
/**
A short description of class Foo.
*/
class Foo
{
}
You can add additional documentation in a file with a .dox extension (let's call it Foo.dox)
/**
More details about Foo.
Maybe you only want to distribute this to your nicer customers.
#class Foo Foo.h
*/
Don't forget to add the .dox file to the INPUT line of your configuration file
INPUT = \
Foo.h Foo.dox

Why is one class not able to recognize the public member variable of another class

I have two classes as follows
Public Class A_one 'This project does not have a dll generated even after a build. Any reason why ?
Public Structure struct
Dim xyz as String
Dim p as String
End Structure
Public Sub xyz(...)
ClassB_one_Obj = New ClassB_one_Obj(SampleStruct)
ClassB_one_Obj.send_struct(sampleStruct)
End Sub
Public Class B_one 'In a different project
Public Sub send_struct(ByVal sampleStruct As A_one.struct) 'Throwing error here **"Type A_one.struct not defined"**
Can anybody explain why i'm getting the error. Is it because I have not added the dll reference of class A_one in Class B_one ?
I tried to add the reference dll of A_one , but was not able to find it either in the obj/bin folders .
Can anybody point me to a work around ?
EDIT/UPDATE : Figured that the class A_one which is the main executable creates objects of Class B_one and that is the reason we can't create an object of class A_one in B_one, since there is a deadlock-like situation.
Is it true that we can't create an object of the main class ( start-up class ) from another class ?
Yes, the error is because project B is not referencing project A, so it doesn't know anything about that type.
From withing Visual Studio, go to the properties of project B, References, Add. If project A is in the same solution as B, add it as as a Project/Solution reference, otherwise you can browse for the DLL. If you use the DLL method, you will have to ensure that the project type of project A is set to be a Class Library and that it has been successfully built. If its not set to be a Class Library project, it will most likely have built as an EXE file (you can add a reference to an EXE file as well, if you need project A to stay as a stand-alone executable).