The procedure used to compile the source code is substantially different from that used in most other open-source software. The most common way to compile a software project relies on the make
utility, and the presence of one or several Makefiles
describing which files are to compiled and linked, and in what order. The process of generating the Makefiles
is often facilitated by other utilities such as autoconf
& automake
. One disadvantage of this approach is that these Makefiles
must be updated every time changes are made to the source code that affect the dependencies and the order of compilation.
In MRtrix, building the software relies on a two-stage process implemented in Python. First, the configure
script should be executed to set the relevant architecture-specific variables (see The configure script for details). Next, the build
script is executed, and is responsible for resolving all inter-dependencies, then compiling and linking all the relevant files in the correct order. This means that any new files added to the source tree will be compiled if needed (according to the rules set out below), without any further action required. In addition, this script is multi-threaded and will use all available CPU cores simultaneously, significantly reducing the time needed to build the software on modern multi-core systems.
NUMBER_OF_PROCESSORS
environment variable before invoking ./build
.The build scripts used to build MRtrix applications are designed to be easy to use, with no input required from the user. This does mean that developers must follow a few fixed rules when writing software for use with MRtrix.
cmd/
folder. For example, if a new application called myapp
is to be written, write the corresponding code in the cmd/myapp.cpp
source file, and the build script will attempt to generate the executable release/bin/myapp
from it. You may want to consult the section Creating a new MRtrix command for information on the contents of a command.lib/
folder should contain only code destined to be included into the MRtrix shared library. This library is intended to provide more generic image access and manipulation routines. Developers should avoid placing more application-specific routines in this folder.src/
folder. The corresponding code will then be linked directly into the executables that make use of these routines, rather than being included into the more general purpose MRtrix shared library.*.cpp
), and the corresponding declarations should be placed in a header file with the same name and the appropriate suffix (*.h
). This is essential if the build script is to resolve the correct dependencies and link the correct object files together.The first step required for building the software is to run the configure
script, which tailors various parameters to the specific system that it is run on. This includes checking that a compiler is available and behaves as expected, that other required packages are available (such as Eigen), whether the system is a 64-bit machine, etc. It is also possible to create distinct co-existing configurations, for example to compile either release and debug code. For details, see The configure script.
This script is responsible for identifying the targets to be built, resolving all their dependencies, compiling all the necessary object files (if they are out of date), and linking them together in the correct order. This is done by first identifying the desired targets, then building a list of their dependencies, and treating these dependencies themselves as targets to be built first. A target can only be built once all its dependencies are satisfied (i.e. all its required dependencies have been built). At this point, the target is built only if one or more of its dependencies is more recent than it is itself (or if it doesn't yet exist). This is done by looking at the timestamps of the relevant files. In this way, the relevant files are regenerated only when and if required.
The following rules are used for each of these steps:
Specific targets can be specified on the command-line, in which case only their minimum required dependencies will be compiled and/or linked. This is useful to check that changes made to a particular file compile without error, without necessarily re-compiling all other associated files. For example:
$ ./build release/bin/mrconvert $ ./build lib/mrtrix.o lib/app.o
If no specific targets are given, the default target list will be generated, consisting of all applications found in the cmd/
folder. For example, if the file cmd/my_application.cpp
exists, then the corresponding target release/bin/my_application
will be included in the default target list.
The special target clean
can be passed to the build
script to remove all system-generated files, including all object files (*.o
), all executables (i.e. all files in the release/bin/
folder), and the MRtrix shared library.
A target is assumed to correspond to an executable if it resides in the release/bin/
folder (the default target list consists of all executables). The dependencies for an example executable release/bin/myapp
are resolved in the following way:
lib/mrtrix-X_Y_Z.so
is added to the listcmd/myapp.o
is added to the listcmd/myapp.cpp
is generated. A header is considered local if it is included using inverted commas rather than angled brackets. For example: cmd/myapp.cpp
includes the header src/histogram.h
, and the file src/histogram.cpp
exists, the object file src/histogram.o
is added to the list of dependencies. Note that object files in the lib/
folder are not added to the list of dependencies, since they should already be included in the MRtrix library (see below).src/histogram.cpp
might also include the header src/min_max.h
. Since the source file src/min_max.cpp
exists, the corresponding object file src/min_max.o
is added to the list.A target is considered to be an object file if its suffix corresponds to the expected suffix for an object file (usually *.o
). The dependencies for an example object file lib/mycode.o
are resolved as follows:
lib/mycode.cpp
is added to the listlib/mycode.cpp
is generated.The list of dependencies for the MRtrix library is generated by adding the corresponding object file for each source file found in the lib/
folder. For example, if the file lib/image/header.cpp
is found in the lib/
folder, the object file lib/image/header.o
is added to the list of dependencies.
lib/
folder. These are all linked together to form the shared library.