.. _ch02-fortran-makefiles: ============================================================= Makefiles ============================================================= Makefiles and the associated program ``make`` are the defacto standard for building software on Unix style machines. Makefiles can also be a little bit arcane in their structure owing to just how long they have been around. The fundamental purpose of makefiles is to define all of the compilation instructions for the source code in a project, and to recognize the dependencies therein. There are more flexible and/or modern tools like `autotools `_, `cmake `_, `meson `_, or others. Most of these ultimately generate Makefiles, but provide an extra layer on top of it that can be helpful for complicated projects with many (external) dependencies that also need to run on many different systems. These tools are pretty clunky on smaller code bases, so we'll ignore them in this class. Makefile Rules ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A makefile takes a set of *rules* in the following format .. code-block:: make target ... : prerequisites ... (tab character) recipe (tab character) ... * A *target* usually refers to a file name that is generated by compiling or linking your code. * A *prerequisite* is a file that is required as input in order to generate the target. These prerequisites may also be targets that have their own dependencies. * A *recipe* is an action that takes place when ``make`` builds a target. You must offset the recipe with a ``tab`` character. This will not work with spaces. - You can technically change the character used for offsetting the recipes, but we're going to ignore that for now. Makefile examples ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Here we provide an example Fortran code ``fullcode.f90`` that consists of a main program and two subroutines: .. literalinclude:: ./codes/multifile/fullcode.f90 :language: fortran :linenos: To illustrate the construction of a Makefile, we'll first break this up into three separate files: .. literalinclude:: ./codes/multifile/main.f90 :language: fortran :linenos: :download:`Download this code <./codes/multifile/main.f90>` .. literalinclude:: ./codes/multifile/sub1.f90 :language: fortran :linenos: :download:`Download this code <./codes/multifile/sub1.f90>` .. literalinclude:: ./codes/multifile/sub2.f90 :language: fortran :linenos: :download:`Download this code <./codes/multifile/sub2.f90>` Below, we study several Makefiles that get successively more sophisticated to compile the code provided in this section. In the first version we write out explicitly what to do for each file. In order to run this ``Makefile``, just type ``make``. This will work as long as your makefile has one of a few a default names like ``Makefile`` or ``makefile``: .. literalinclude:: ./codes/multifile/Makefile :language: make :linenos: :download:`Download this code <./codes/multifile/Makefile>` This is pretty tedious, and not much better than just writing all of the compiler calls on the command line. In the second version there is a general rule for creating ``.o`` files from ``.f90`` files, called *inference rules* or *pattern rules* (see more `pattern rules `_). There are also an old style rules called *suffix rules* which we will not use in our course (see `article-suffix `_). The last line (i.e., recipe) has the special macro ``$<`` which corresponds to the name of the first prerequisite (i.e., ``*.f90`` files). There are seven frequently used `automatic variables `_. In order to run this non-default ``Makefile2``, you need to type in ``make -f Makefile2``: .. literalinclude:: ./codes/multifile/Makefile2 :language: make :linenos: :download:`Download this code <./codes/multifile/Makefile2>` In the third version we define the variable ``OBJECTS`` so that we only have to write out this list once, which minimizes the chance of introducing errors: .. literalinclude:: ./codes/multifile/Makefile3 :language: make :linenos: :download:`Download this code <./codes/multifile/Makefile3>` In the fourth version, we place the Fortran compiler into the variable ``FC``. This allows a user to override the choice of compiler if they want to. Additionally, compiler flags for debugging and optimization (see `this `_ StackOverflow answer for more information about the ``BUILD`` variable), as well as a linker flag (blank in this example), have been added: .. literalinclude:: ./codes/multifile/Makefile4 :language: make :linenos: :download:`Download this code <./codes/multifile/Makefile4>` Next we add a ``phony`` target ``clean`` that removes the files created when compiling the code in order to facilitate cleanup. It is *phony* because it does not create a file named ``clean``, rather ``clean`` is an *action*. To run ``clean``, type ``make -f Makefile5 clean``: .. literalinclude:: ./codes/multifile/Makefile5 :language: make :linenos: :download:`Download this code <./codes/multifile/Makefile5>` Fancier things are also possible, for example automatically detecting all the ``.f90`` files in the directory to construct the list of ``SOURCES`` and ``OBJECTS``: .. literalinclude:: ./codes/multifile/Makefile7 :language: make :linenos: :download:`Download this code <./codes/multifile/Makefile7>` Further reading --------------- * ``_ * ``_ * ``_ * ``_ * `remake `_, a make debugger * ``_, a makefile to produce multiple executables