[CMake] C header file cross dependency

Patrick Boettcher patrick.boettcher at posteo.de
Wed May 25 05:39:02 EDT 2016


On Mon, 23 May 2016 13:49:14 +0000
Wagner Martin <Martin.Wagner at neuberger.net> wrote:

> Hi @all,
> 
> I'm quite new to CMake. If I've made a mistake or something is much
> easier to solve, please tell me.
> 
> I'd like to use CMake in embedded development (Build System: Linux,
> Target: ARM Microcontroller) to get rid of complicated makefiles.

Good thing! 

> We're building multiple devices where stuff like embedded rtos and
> peripheral drivers are identical, so I'd like to separate this part
> from the user application. I've achieved that by creating an object
> library out of all source and header files, and packing those files
> using CPack. This archive is then statically linked against the user
> application.
> 
> So far this worked fine. However, now I have to use driver functions
> in the rtos source code and vice versa, resulting in
> cross-dependencies for header files:
> 
> <drivers/uart.c>
> 
> #include uart.h
> #include terminal.h
> 
> function() {}
> 
> <os/terminal.c>
> 
> #include terminal.h
> #include uart.h
> 
> function() {}
> 
> How do I resolve something like this? Right now CMake evaluates the
> compiler includes in the order that subdirectories are added. This
> gives me an compilation error in uart.c that terminal.h cannot be
> found.

This is not a cmake-problem, but seems to be a code-structure-issue.

I'm guessing here: if terminal needs the uart-code shouldn't it be the
uart-code filling in a terminal-function. Interface vs. implementation?
Could you elaborate more on how terminal and uart are linked?

Regarding cmake: I suggest you stop using include_directories() and
start using target_include_directories() and
target_compile_definitions() instead of add_definitions().

Limiting yourself to this way of doing libraries and targets, cmake will
force you to structure your code in a more standard way - and will
provide you with clean visibility between different targets.

Could you provide a working, stripped down example to show the problem
provided via github (in an example repo).

More comments below.
 
> Some excerpt of my project. I've tried to keep the example as simple
> as possible.
> 
> My directory structure looks something like that:
> /
> CMakeLists.txt
> src +
>     +CMakeLists.txt            (1)
>     +drivers+
>     |       +uart.c
>     |       +uart.h
>     |       +...
>     |       +CMakeLists.txt    (2)
>     +os-----+
>     |       +terminal.c
>     |       +terminal.h
>     |       +...
>     |       +CMakeLists.txt    (3)
> 
> 
> (1):
> 
> SET(drivers "drivers")
> SET(terminal "terminal")
> 
> SET(drivers_lib ${drivers})
> SET(terminal_lib ${terminal})
> 
> SET(ARCHIVE_INSTALL_DIR lib)
> SET(INCLUDE_INSTALL_DIR include)
> 
> SET(headers_private "_headers_private") # internal headers
> SET(headers_public "_headers_public")   # public headers go into
> package
> 
> ADD_SUBDIRECTORY(${drivers})
> ADD_SUBDIRECTORY(${terminal})

I think it is common practice now to use lower-case for cmake-commands
now. 

> ## drivers
> 
> ## ---- Sources
> -------------------------------------------------------------------
> SET(sources "uart.c"
> )
> 
> ## ---- Header includes
> -----------------------------------------------------------
> SET(headers "${CMAKE_CURRENT_SOURCE_DIR}/" 
> )
> SET(${drivers}${headers_public} ${headers} PARENT_SCOPE)
> 
> INCLUDE_DIRECTORIES(${headers} 
>                     ${${terminal}${headers_public}}
> )

While the ${${var}${var2}} (seems to) work, it is error-prone, IMHO.

Standard cmake-commands can work with relative paths and are evaluating
them correctly taking into account ${CMAKE_CURRENT_SOURCE_DIR} (most of
the time. So you could use ../uart in terminal/ - but it would be
better if it comes indirectly via target_include_directories() and
target_link_libraries()

>[..]
> 
> And finally this creates the package in root directory CMakeLists.txt:
> 
> SET(CPACK_PROJECT_CONFIG_FILE ${CMAKE_BINARY_DIR}/CPackOptions.cmake)
> # CPackOptions.cmake contains package file name SET(CPACK_GENERATOR
> "TBZ2") INCLUDE(CPack)

Due to the circular header-dependency the binaries of terminal and uart
should have the same mutual dependency. In this case you could build
them in within one target.

regards
--
Patrick.



More information about the CMake mailing list