Build system for an embedded C/C++ project

筅森魡賤 提交于 2019-12-04 05:14:42

The conventional way of achieving that would be to place the source code for each module into a separate directory. Each directory can contain all the source and header files for the module.

The public header for each module can be placed into a separate, common directory of headers. I'd probably use a symlink from the common directory to the relevant module directory for each header.

The compilation rules simply state that no module may include headers from other modules except for the headers in the common directory. This achieves the result that no module can include headers from another module - except for the public header (thus enforcing the private barriers).

Preventing cyclic dependencies automatically is not trivial. The problem is that you can only establish that there is a cyclic dependency by looking at several source files at a time, and the compiler only looks at one at a time.

Consider a pair of modules, ModuleA and ModuleB, and a program, Program1, that uses both modules.

base/include
        ModuleA.h
        ModuleB.h
base/ModuleA
        ModuleA.h
        ModuleA1.c
        ModuleA2.c
base/ModuleB
        ModuleB.h
        ModuleB1.c
        ModuleB2.c
base/Program1
        Program1.c

When compiling Program1.c, it is perfectly legitimate for it to include both ModuleA.h and ModuleB.h if it makes use of the services of both modules. So, ModuleA.h cannot complain if ModuleB.h is included in the same translation unit (TU), and neither can ModuleB.h complain if ModuleA.h is included in the same TU.

Let us suppose it is legitimate for ModuleA to use the facilities of ModuleB. Therefore, when compiling ModuleA1.c or ModuleA2.c, there can be no issue with having both ModuleA.h and ModuleB.h included.

However, to prevent cyclic dependencies, you must be able to prohibit the code in ModuleB1.c and ModuleB2.c from using ModuleA.h.

As far as I can see, the only way to do this is some technique that requires a private header for ModuleB that says "ModuleA is already included" even though it isn't, and this is included before ModuleA.h is ever included.

The skeleton of ModuleA.h will be the standard format (and ModuleB.h will be similar):

#ifndef MODULEA_H_INCLUDED
#define MODULEA_H_INCLUDED
...contents of ModuleA.h...
#endif

Now, if the code in ModuleB1.c contains:

#define MODULEA_H_INCLUDED
#include "ModuleB.h"
...if ModuleA.h is also included, it will declare nothing...
...so anything that depends on its contents will fail to compile...

This is far from automatic.

You could do an analysis of the included files, and require that there is a loop-less topological sort of the dependencies. There used to be a program tsort on UNIX systems (and a companion program, lorder) which together provided the services needed so that a static (.a) library could be created that contained the object files in an order that did not require rescanning of the archive. The ranlib program, and eventually ar and ld took on the duties of managing the rescanning of a single library, thus making lorder in particular redundant. But tsort has more general uses; it is available on some systems (MacOS X, for instance; RHEL 5 Linux too).

So, using the dependency tracking from GCC plus tsort, you should be able to check whether there are cycles between modules. But that would have to be handled with some care.

There may be some IDE or other toolset that handles this stuff automatically. But normally programmers can be disciplined enough to avoid problems - as long as the requirements and inter-module dependencies are carefully documented.

For a general solution, I'd fully recommend to go with Jonathan Leffler's solution. However, if you absolutely need an automated test whether your modules are self-contained and isolated, you might give Debian's build system a try.

Package each module into a Debian package (which is done very quickly when it's already autoconf'd), declare Build-Depends properly and build the packages inside a pbuilder environment. This ensures that only public headers of each module are available (because only those are in the .deb packages that are installed by pbuilder to build the other packages) and there are excellent tools to look at Debian package trees and make sure they are cycle-free.

However, this is probably over-kill. Just stating it for completeness and the case you definitely need an automated solution.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!