Build system#

This page documents the MESA build system. It is only relevant if you want to modify the build system, or use some of the MESA modules outside of MESA. For building MESA itself without modifications to the build system, see Installing MESA.

The MESA build system uses make, with some additional (shell) scripts. All relevant files can be found in the make subfolder, except for the main Makefile in the repository root, and the module specific make files (see Module build configuration).

Running make in the root folder will build all the modules, install relevant files, and run all the associated module tests (test suite tests will not be run). To build an individual module and all its dependencies, run make MODULE_NAME. Append -install or -check to run those steps. If you don’t want to try to build the dependencies, cd to the folder of that module and run make there.

If you don’t want to run any tests, but compile all the modules and extract all the necessary data, run make all-build all-extract-data.

Build options#

The build system exposes several options to control the build process. If you want to change these options, give them as arguments to make, e.g. make BUILD_DIR=my_build_dir or set them in your environment.

Warning

If you change any of these options, make sure the clean the build outputs with make clean. Otherwise the change to the option may not apply at all, or even causing a weird hybrid build.

  • BUILD_DIR: Where all compiled files will end up, default is build. The output from each module’s build will be in a subfolder with the module’s name.

  • COMPILER: Which compiler to use. Supported options are gfortran (default).

  • PROFILE: Which set of compiler flags to use, mostly to control optimizations. Supported options are release (enable optimizations), release-with-dbg-info (enable optimizations, but keep debug info, default), and debug (disable most optimizations, keep debug info).

  • DYNAMIC: Whether to generate dynamic or static libraries for each of the modules. Defaults to no (set to yes to enable).

  • WITH_OPENMP: Whether to use openmp. Defaults to yes (any other value will disable openmp).

  • WITH_CRLIBM: Whether to use crlibm for certain math operations. Defaults to yes (any other value will disable crlibm).

  • WITH_FPE_CHECKS: Whether to enable FPE checks for NaNs, overflows, and division by zero. Defaults to no (set to yes to enable).

  • WITH_GYRE: Whether to build GYRE and include support for GYRE in MESA. Defaults to yes (any other value will disable GYRE).

  • WITH_ADIPLS: Whether to build ADIPLS and include support for ADIPLS in MESA. Defaults to yes (any other value will disable ADIPLS).

  • WITH_PGSTAR: Whether to include support for showing and saving plots with pgplot. Defaults to yes (any other value will disable pgstar/pgplot).

Module build configuration#

Each MESA module has its own configuration Makefile. This make file is located in the root directory of the module. For example, in the case of the eos module, this make file is eos/Makefile. This make file includes configurations for how to build, test, and install the module. This can be both in the form of make variables and rules.

The following properties control the main compilation of the module:

  • MODULE_NAME: The name of the module. (e.g. eos)

  • SRCS: Source files (.f, .f90) that are part of this module. Include files (.inc, .dek) should not be included in this list.

  • INTERNAL_DEPENDS_ON: Which MESA modules this module directly depends on. Transitive dependencies do not need to be filled in here.

  • EXTERNAL_DEPENDS_ON: Which external packages (e.g. lapack) this module depends on.

  • BINTYPE: What the eventual output of the build should be, can be lib, executable or manual. The manual option skips the generation of the final binary object. This is used in the gyre module, as more than one library is created.

  • LIB_NAMES: Which output libraries are produced in case BINTYPE is set to manual.

  • INCLUDE_DIRS: In which directories are the source files found, prefixed by -I. By default, this is -Ipublic -Iprivate.

The following properties control building and running the tests of the module:

  • SRCS_CHECK: Source files that are part of the test binary. Typically, all the .f or .f90 files under module-name/test/src.

  • CHECK_RESULTS_GOLDEN: File to compare the output from the test binary against. If this is left empty, no tests will be run.

  • CHECK_DIFF_PROG: For setting a different a program to compare the output to the golden output (e.g. ndiff). By default this is diff -b.

  • CHECK_FILTER_PROG: Filters the output of the executable before running the diff program. By default this uses grep to remove output from caches being initialized.

Finally, we have the install settings, which control what files are made available to other modules:

  • MODULES: Which mod files should be made available.

  • INSTALL_INCLUDES: Which additional include files should be made available.

Conditional compilation#

Sometimes you want to have certain files included in the compilation depending on other variables. This can be done with the usual make file syntax. For example, see the following snippet from math/Makefile which adds a source file and dependency based on whether the variable WITH_CRLIBM is set to yes.

ifeq ($(WITH_CRLIBM),yes)
SRCS += public/math_lib_crmath.f90
EXTERNAL_DEPENDS_ON += crmath
else
SRCS += public/math_lib_intrinsic.f90
endif

All variables mentioned above can be modified in this way.

Preprocessing#

Some of the modules require some preprocessing of the FORTRAN source code. This requires setting up a make target for these files, and add the generated files to the SRCS_GENERATED or SRCS_CHECK_GENERATED. These source files will be automatically prefixed with the path to the build directory, so make sure that the output files from the preprocessing are written to the build directory. The following snippet shows an example from the mtx module (currently no longer used, but can serve as inspiration). Be careful copying the text, as all tabs will have converted to spaces:

SRCS_GENERATED := private/my_lapack95_dble.f90 \
                  private/my_lapack95_quad.f90
SRCS_CHECK_GENERATED := \
        test/src/test_block_tridiagonal_dble.f90 \
        test/src/test_block_tridiagonal_quad.f90

include ../make/Makefile

# Custom build steps (this needs to come after the include statement,
#  otherwise the BUILD_DIR_MODULE variable is not set)

$(BUILD_DIR_MODULE)/private/my_lapack95_dble.f90: private/my_lapack95.F90 | $(BUILD_DIR_MODULE)/private/
    # Note: PREPROCESS just calls the C preprocessor, and is set in compile-settings-*.mk
        $(PREPROCESS) -DDBLE $^ > $@

$(BUILD_DIR_MODULE)/private/my_lapack95_quad.f90: private/my_lapack95.F90 | $(BUILD_DIR_MODULE)/private/
        $(PREPROCESS) $^ > $@

$(BUILD_DIR_MODULE)/test/src/test_block_tridiagonal_dble.f90: test/src/test_block_tridiagonal.f90 | $(BUILD_DIR_MODULE)/test/src/
        $(PREPROCESS) -DDBLE $^ > $@

$(BUILD_DIR_MODULE)/test/src/test_block_tridiagonal_quad.f90: test/src/test_block_tridiagonal.f90 | $(BUILD_DIR_MODULE)/test/src/
        $(PREPROCESS) $^ > $@

Integrating modules in another piece of software#

Since MESA is written in a modular way, it is possible to take some of the piece of the MESA source code and integrate them in your own project. You will need at least the make folder which contains all the build files, the main Makefile in the root of the repository, the source of the module(s) you want to include, and the sources of all the dependent modules. If these modules need data files (e.g. eos tables), you will need to ensure you have them available as well. It is recommended to keep the same source tree, otherwise certain paths in the various make files may no longer point to the right directories. One change that will be necessary, is to edit the list of all modules in make/subdirs.mk. This is used by the make files to establish the right dependencies. However, if some of the folders in that list do not exist, you will get errors. In order to build and test the modules, run make -C MESA_SOURCE_TREE MODULE1 MODULE2 ..., where MESA_SOURCE_TREE refers where in your project you have copied the MESA source code. If you want to consolidate build directories of your project and the MESA modules, add BUILD_DIR=LOCATION_OF_BUILD_DIR where LOCATION_OF_BUILD_DIR is relative to the MESA source directory.

The nitty-gritty details#

When running make on a clean MESA source tree, the following happens:

  1. The main Makefile gets loaded, which on its turn will load all relevant dependent make files in make/. These additional make files contain instructions for setting up the build dir, getting the version of the source code, and collecting dependencies between the modules.

  2. For each module listed in make/subdirs.mk, the main make process will call into the module specific make file (MODULE/Makefile) and request the dependent modules of that module. A make file containing dependencies between all the modules is then generated by the make/gen-folder-deps script.

  3. The version number is written to data/version_number if it does not exist with git describe. As source distributions will not contain any git information, this file should be precreated.

  4. For each module that needs building, make will run their builds and tests in parallel, obeying the dependencies set in step 2.

Within each module’s separate make process, the following steps will happen:

  1. The main make file for each module will set the relevant settings and load make/Makefile, which contains the core build logic.

  2. Some initialization is repeated, such as the setup of the build directory to support building modules separately.

  3. Load relevant other make files, such as those for setting the correct dependencies between source files, loading compiler settings, link commands, …

  4. PKG_CONFIG_PATH is set to all known module’s pkg-config build folders (build/module_name/lib/pkg-config). This path is generated by the make/gen-pkgconfig-path script (since it is much cleaner to do this in bash than in the make files themselves).

  5. make/gen-compile-tree is called with all FORTRAN source files for that module, and is given all the necessary include paths. It will parse (in a simplified way) the source files and generate a make file that contains the necessary dependencies between all the modules. Meaning that if a module abc contains use def, the compilation of module abc will need to wait until the compilation of def is done. The resulting make file is then loaded by make.

  6. The source files get compiled, and the final object file gets linked according to which type of object is selected in the module config.

After the module is built, all relevant files are installed and the module-specific tests will run:

  1. Copy the public modules, include files, and compiled library to include and lib

  2. Compile the test binary from the test source files and link it with the object file generated previously.

  3. Run the test binary and compare the output to a golden output specified by the module config.

Parallel compilation#

In order to improve parallelisation, the compilation of each source file is split into two steps: generating the .mod files, and actually compiling the source code. FORTRAN .mod files describe the interface of a certain module, but do not contain the actual compiled code. In order to build dependent modules, only this .mod file is necessary. Since this file is generated much faster than compiling the module, the build system generates them in a separate step, which allows the compilation of the dependent module to start almost immediately.

Work directory#

Each work directory has its own build directory in the work directory itself to prevent conflicts with other work directories. The setup is similar to a normal MESA module, but does less work. For example, it will not call into the MESA build system to build required modules. Each module is also added to the include and library paths, there is no option to limit this. The details can be found in the make/work.mk script.