[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