|Large multiple file programs|
As a consequence of these principles, the compilation process for large projects is easy to understand: the programmer should just make sure to carefully include the correct include files for every individual file in the project. The compiler will then take care of determining the various dependencies and compiling the files in the project in the right order and in parallel whenever possible.
Notice that these rules are not satisfied in several other
programming languages such as C or C++: in this case, there is usually
a makefile for the project which describes the dependencies
and all kinds of compiler and linker flags which are necessary for
building the executable.
In the case when some of the functionality of a
Other files can be included into a given
When including a file a.mmx into the file b.mmx using
all public functions, classes and categories from the file a.mmx are made available in the file b.mmx. Moreover, for any chain of public inclusions b.mmx c_1.mmx c_n.mmx (we say that b.mmx is indirectly and publicly included by c_n.mmx), the public functionality of a.mmx is also available in c_n.mmx.
Public inclusions thus induce a dependency of b.mmx on a.mmx as well as of c_n.mmx on a.mmx for any file c.mmx which indirectly includes b.mmx. When building a large project in parallel, this means that both b.mmx and c.mmx will have to be recompiled whenever a change occurs in the public interface of a.mmx.
In order to reduce the number of dependencies inside a large project, it is possible to use the mechanism of private inclusions whenever appropriate. When including a file a.mmx into the file b.mmx using
private include "a.mmx";
all public functions, classes and categories from the file a.mmx are made available in the file b.mmx. However, the functionality of a.mmx remains hidden for any other file c.mmx which directly or indirectly includes a.mmx (except when c.mmx includes a.mmx itself, or via some other file different from b.mmx, of course). On the other hand, if a.mmx indirectly and publicly includes a file A.mmx, then A.mmx remains an indirect (although private) inclusion of b.mmx, so all public functionality of A.mmx is also available in b.mmx.
In summary, a private inclusion of a.mmx in b.mmx still induces a dependency of b.mmx on a.mmx, but the transitivity of the inclusion relation is broken.
When using the mechanism of private inclusion, we still introduce dependencies for the build process: whenever the public interface of the included file a.mmx changes, the file b.mmx where the inclusion occurred needs to be recompiled.
On some occasions, none of the functionality of a.mmx is actually needed in b.mmx or any other file which includes b.mmx (directly or indirectly). Indeed, it may happen that we just want to ensure that some initialization code present in a.mmx is executed before we start executing b.mmx. For instance, the file a.mmx might initialize some table which was declared in a file A.mmx which is included both by a.mmx and by b.mmx, and such that the code in b.mmx will only work correctly after initialization of this table.
Whenever the above situation occurs, we may perform a virtual inclusion of a.mmx into b.mmx
virtual include "a.mmx";
Virtual inclusions do not create any dependencies for the build process, but they do influence the list of files which need to be compiled and linked, as well as the order in which the various files of the project are executed (see section ? below).
Whatever inclusion modes are used, no circular chain of inclusions is
In the case when some code in a file b.mmx depends on some code in the file a.mmx and vice versa, the programmer will have to explicitly include prototypes for the required functionality in one of the files. Class prototypes are declared using the syntax
class Container (P_1: T_1, ..., P_n: T_n);
Function prototypes are declared using the syntax
fun (arg_1: T_1, ..., arg_n: T_n);
Duplicate inclusions. Initialization only once, for the first inclusion.