iPXE
#ifdef considered harmful

Overuse of #ifdef has long been a problem in Etherboot.

Etherboot provides a rich array of features, but all these features take up valuable space in a ROM image. The traditional solution to this problem has been for each feature to have its own #ifdef option, allowing the feature to be compiled in only if desired.

The problem with this is that it becomes impossible to compile, let alone test, all possible versions of Etherboot. Code that is not typically used tends to suffer from bit-rot over time. It becomes extremely difficult to predict which combinations of compile-time options will result in code that can even compile and link correctly.

To solve this problem, we have adopted a new approach from Etherboot 5.5 onwards. #ifdef is now "considered harmful", and its use should be minimised. Separate features should be implemented in separate .c files, and should always be compiled (i.e. they should not be guarded with a #ifdef MY_PET_FEATURE statement). By making (almost) all code always compile, we avoid the problem of bit-rot in rarely-used code.

The file config.h, in combination with the make command line, specifies the objects that will be included in any particular build of Etherboot. For example, suppose that config.h includes the line

   #define CONSOLE_SERIAL
   #define DOWNLOAD_PROTO_TFTP

When a particular Etherboot image (e.g. bin/rtl8139.zdsk) is built, the options specified in config.h are used to drag in the relevant objects at link-time. For the above example, serial.o and tftp.o would be linked in.

There remains one problem to solve: how do these objects get used? Traditionally, we had code such as

    #ifdef CONSOLE_SERIAL
      serial_init();
    #endif

in main.c, but this reintroduces #ifdef and so is a Bad Idea. We cannot simply remove the #ifdef and make it

because then serial.o would end up always being linked in.

The solution is to use linker tables .