Create a system for an embedded C / C ++ project

I am looking for a high-level build system / tool that will help organize my embedded C project into “modules” and “components”. Please note that these two terms are very subjective, so my definitions are given below.

  • A module is a cohesive collection of c and h files, but with only one public h file, which is visible to other modules.
  • A component (or layer), on the other hand, is a collection of modules (for example, application layer, library layer, driver layer, RTOS layer, etc.).

The assembly system / tool should -

  • Prevention of cyclic dependencies between components and modules (cyclic dependencies inside modules are in order)
  • prevent access to closed module barriers. If other modules try to include a header file that is closed to the module, the build system should throw an error. However, files within a private barrier should be able to include other files in this file.
  • support for creating and running unit tests automatically (fast feedback loop for TDD) on the host
  • tests of auxiliary modules to run on the target simulator
  • static code analysis support
  • creating support code
  • code support duplication detection (DRY compliance)
  • beautification code support
  • support for generating unit test code coverage metrics
  • support for generating code quality metrics
  • platform independent

I could write my own building tool and spend a lot of time on it. However, this is not my area of ​​knowledge, and I would prefer not to reinvent the wheel if someone had already created such a tool.

+6
source share
2 answers

The traditional way to achieve this would be to put the source code for each module in a separate directory. Each directory can contain all the source and header files for the module.

An open header for each module can be placed in a separate common header directory. I would probably use a symbolic link from the shared directory to the appropriate module directory for each header.

The compilation rules simply state that no module can include headers from modules other than headers in a shared directory. This leads to the fact that no module can include headers from another module, except for the public header (thus, using private barriers).

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

Consider a couple of modules ModuleA and ModuleB and program1, which 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 legal for him to include both ModuleA.h and ModuleB.h if he uses the services of both modules. Thus, ModuleA.h cannot complain if ModuleB.h is included in the same translation unit (TU), and not one of ModuleB.h modules can complain if ModuleA.h is included in the same TU.

Assume that it is legal to use ModuleB facilities for ModuleA. Therefore, when compiling ModuleA1.c or ModuleA2.c, there can be no problem with the inclusion of modules Module.h and ModuleB.h.

However, to prevent circular dependencies, you should be able to prohibit the use of ModuleB1.c and ModuleB2.c code with ModuleA.h.

As far as I can tell, the only way to do this is by some method that requires a private header for ModuleB that says “ModuleA is already enabled”, although it is not, and it is included before ModuleA.h when- either included.

The skeleton 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 can analyze the included files and require that it contains non-point topological dependency types. Previously, on UNIX systems (and the accompanying lorder program), the tsort program was tsort , which together provided the necessary services so that you could create a static ( .a ) library containing object files in order that did not require re-scanning the archive. The ranlib program, and ultimately ar and ld took on the responsibility of managing the rescan of one library, thereby making lorder particularly redundant. But tsort has a more general use; it is available on some systems (e.g. MacOS X, RHEL 5 Linux).

So, using GCC dependency tracking plus tsort , you should be able to check if loops between modules exist. But this will need to be handled with some caution.

There may be some IDE or other toolkit that processes this material automatically. But, as a rule, programmers can be disciplined enough to avoid problems - provided that the requirements and intermodule dependencies are carefully documented.

+4
source

For a general solution, I totally recommend going with the decision of Jonathan Leffler. However, if you absolutely need to automatically test the autonomy and isolation of your modules, you can try the Debian build system.

Package each module into a Debian package (which runs very quickly when it was already autoconfigured), declare Build-Depends correctly and create packages inside the pbuilder environment. This ensures that only the public headers of each module are available (because only those are in the .deb packages that pbuilder installs to create other packages), and there are excellent tools for viewing Debian package trees and make sure free.

However, this is probably too much. Just stating this for completeness and occasion, you definitely need an automatic solution.

+2
source

All Articles