The L4Re build system is based on Makefiles, and the mk directory within the idl4re software distribution contains a collection of make-compatible files that integrate the idl tool and interface files with the L4Re build system so that the tool is invoked automatically when necessary.
To write or adapt a package Makefile, some additional definitions and statements are needed to introduce the additional processing steps that take interface descriptions and generate source code from them when needed.
This document describes the form a package Makefile should take and introduces the required elements to integrate interface code generation into the package build process.
The general form of a suitable Makefile employing interface generation follows that broadly used within L4Re, with a top-level package employing the following definitions:
PKGDIR ?= . L4DIR ?= $(PKGDIR)/../..
Appropriate target and build mode definitions may be specified. For example, for a program:
TARGET = myserver MODE = shared
For a library:
TARGET = libserver.a libserver.so
Various locations and the inclusion of some rules are useful to define at this point:
IDL_DIR = $(L4DIR)/pkg/libsystypes/idl IDL_MK_DIR = $(L4DIR)/idl4re/mk IDL_BUILD_DIR = . IDL_EXPORT_DIR = . include $(IDL_MK_DIR)/idl.mk
(These are described below.)
Usage of interface descriptions should be defined. For example, for some interfaces employed by client programs in the C language:
CLIENT_INTERFACES_C = file mapped_file
For interfaces used by C++ server programs:
SERVER_INTERFACES_CC = dataspace dataspace_factory
These interface definitions are then complemented with definitions that expand to the source files involved. For example:
CLIENT_INTERFACES_SRC_C = $(call interfaces_to_client_c,$(CLIENT_INTERFACES_C))
Or:
SERVER_INTERFACES_SRC_CC = $(call interfaces_to_server_cc,$(SERVER_INTERFACES_CC))
After these definitions, it is convenient to provide the plain source files that are present in the package. For example:
PLAIN_SRC_C = routines.c main.c
With this, it is possible to provide the normal source file definitions which will be a combination of the plain source files with the ones providing support for interfaces.
For example, for a client program employing generated C language files:
SRC_C = $(PLAIN_SRC_C) $(CLIENT_INTERFACES_SRC_C)
To make sure that any required headers are generated before attempts are made to compile the source files, a special rule is required at the end of the file. This is mentioned below.
Library-related and header-related definitions are typically needed. For example:
REQUIRES_LIBS = libipc libsystypes PRIVATE_INCDIR = $(IDL_BUILD_DIR) $(IDL_EXPORT_DIR)
As usual, the Makefile ends with the appropriate role-specific rule inclusion. For example, for programs:
include $(L4DIR)/mk/prog.mk
For interface-driven file generation, an accompanying statement is also required:
include $(IDL_MK_DIR)/interface_rules.mk
This must appear after the role-specific inclusion statement.
Finally, to coordinate the generation of header files with compilation, a rule must be added after the L4Re build system include statements. This must make the plain source files dependent on generated files. For example:
$(PLAIN_SRC_C): $(CLIENT_INTERFACES_SRC_C)
Or, for C++ server code, using the above naming conventions:
$(PLAIN_SRC_CC): $(SERVER_INTERFACES_SRC_CC)
The following sections describe the different elements of this basic arrangement in more detail.
In a Makefile used to build a L4Re component, the following settings can be used to configure the generation of code from interfaces.
Variable | Purpose |
IDL_DIR | Finding .idl files to be processed |
IDL_MK_DIR | Finding the idl4re/mk directory and rule files |
IDL_BUILD_DIR | The location of generated code for this component |
IDL_EXPORT_DIR | Exporting interface headers |
The roles of the variables can be summarised in a diagram:
Typically, the following kinds of values will be used:
Variable | Value |
IDL_DIR | A directory containing interfaces |
IDL_MK_DIR | $(L4DIR)/idl4re/mk |
IDL_BUILD_DIR | . |
IDL_EXPORT_DIR | (See below for discussion.) |
In the case of IDL_EXPORT_DIR, the variable needs to indicate the location of interface header files that can be referenced by code needing to use the component interfaces. Where such code resides in the same package, the current directory (.) can be used. Where such code resides in other packages, a location is required to export the generated files to those packages.
To avoid mixing up any exported generated interface headers with original non-generated headers, a value of the following form can be used:
$(OBJ_BASE)/include/contrib/$(CONTRIB_INCDIR)
Here, a header called file_interface.h would be exported and included using just this simple name. However, in some cases, there may be other packages exporting headers of the same name, and this may result in confusion.
The solution to this is to organise headers (of any kind) in collections. Here, the header could be placed inside a directory called fsserver and be included as fsserver/file_interface.h. This would be achieved by using a location of the following form:
$(OBJ_BASE)/include/contrib/$(CONTRIB_INCDIR)/fsserver
Immediately after the settings, it is typically useful to define some filename transformations:
include $(IDL_MK_DIR)/idl.mk
These make it easier to define the various interfaces used by a component and to derive the filenames involved using transformation functions such as the following:
$(call interfaces_to_client_cc,$(CLIENT_INTERFACES_CC))
Remember that a conventional package Makefile will be concerned with source files and their compilation. Since interface files will be used to generate files that do not initially exist, knowledge about the names of these generated source files needs to be applied to tell the build system which files it will ultimately need to compile.
Various rules are applied in the build system to make sure that source files are then handled appropriately.
After the settings and the inclusion of the idl.mk file, it is appropriate to define certain variables describing the use of individual interfaces:
Variable | Interface Usage |
CLIENT_INTERFACES_C | C language client code |
CLIENT_INTERFACES_CC | C++ language client code |
SERVER_INTERFACES_C | C language server code |
SERVER_INTERFACES_CC | C++ language server code |
These variables reference .idl files containing interface descriptions. For example:
SERVER_INTERFACES_CC = dataspace mapped_file
This indicates that server code in C++ will need to be generated from dataspace.idl and mapped_file.idl, and rules are generated to ensure that make becomes aware of this need.
The variables are then employed in filename transformations, described in more detail elsewhere in this document.