==================================== Makefiles ==================================== We use `makefiles` to organize code compilation and execution. This page provides a brief tutorial to create and work with `makefiles` for small to medium-sized `Fortran 90/95`__ projects on `Unix/Linux OS`__. I will introduce you to makefiles through simple fortran examples. With some modifications, you can use makefiles for other programming languages, such as C/C++, provided their compilers can be run with a shell command. It is to be noted that "make" is not limited to manage the execution of programs. It can be used to do other tasks/projects involving multiple files, where some files must be updated automatically whenever we modify other files. As an example, I use "make html" to generate these course webpages. Let us start off with the following three Fortran source files, consisting of one main file and two subroutines: .. csv-table:: ".. literalinclude:: ../hpsc2020/Labs/lab2/main.f90 :language: fortran :linenos:",".. literalinclude:: ../hpsc2020/Labs/lab2/sub1.f90 :language: fortran :linenos:",".. literalinclude:: ../hpsc2020/Labs/lab2/sub2.f90 :language: fortran :linenos:" .. |vspace| raw:: latex \vspace{5mm} |vspace| The goal is to create a `makefile` that * compiles, links, and executes these three source codes; * makes necessary updates to some or all files if some other files are modified; * and pehaps does some other useful things, such as graphing. In a Fortran program, the executable file is updated from object files, which are in turn made by compiling source files. Once we create a `makefile`, each time we change/modify some source files, we can use the following shell command to execute the `makefile` and perform all necessary recompilations and updates: .. code-block:: none $ make -f Makefile-name where `Makefile-name` stands for the name of the `makefile` file that we want to use. The `make` program uses the latest version of file modifications to decide which of the files in the `makefile` need to be updated. For example, if a subroutine file has changed, each Fortran source file that calls this subroutine file must be recompiled as well. In what follows we consider and study several `makefiles` that get successively more sophisticated to compile, link, and execute these three Fortran codes. **Note:** You can find the three source files (`main.f90`, `sub1.f90`, `sub2.f90`) and the follwoing `makefiles` in the course repository in "Labs/lab2" directory. Makefile1 ------------------ In the first version we write out explicitly what to do for each file: .. literalinclude:: ../hpsc2020/Labs/lab2/Makefile1 :language: make :linenos: Notes: * A makefile consists of a few "rules" with the following structure: .. code-block:: none TARGET : PREREQUISITE1 PREREQUISITE2 ... COMMAND1 COMMAND2 ... A "target" is usually the name of a file that is generated by a program; examples of targets are executable or object files (as in this makefile). A target can also be the name of an action to carry out, such as "clean" or "graph" (see bellow). A "prerequisite" is a file that is used as input to create the target. A target often depends on several files. A "command" is an action that `make` carries out. A rule may have more than one command, each on its own line. Note that you need to put a tab character at the beginning of every command line. So, make sure you use a ``tab`` for indent, not a space. * Here, `output.txt` dependes on `code.x`, which in turn depends on three `.o` files, which in turn depend on three `.f90` source files. Dependencies are checked by date-stamp. For instance if the `.f90` is newer than the `.o` file, `make` recreates the `.o` file. Makefile2 ------------------ In the second version we use a single rule for creating all `.o` files from all `.f90` files: .. literalinclude:: ../hpsc2020/Labs/lab2/Makefile2 :language: make :linenos: Makefile3 ------------------ The three source files are compiled in the same way. In the previous version we had to write out the list of `.o` files twice. This may increase the chance of introducing errors. We can avoid this using "macros": .. literalinclude:: ../hpsc2020/Labs/lab2/Makefile3 :language: make :linenos: FC and OBJECTS are macros (or makefile variables). They are used by $(FC) and $(OBJECTS) in the rules. Makefile4 ------------------ In the fourth version we add a Fortran compile flag (``O3`` for level 3 optimization). We also add a phony target `clean` that removes the files created when compiling the code (`.o` and `.exe` files) in order to facilitate cleanup. .. literalinclude:: ../hpsc2020/Labs/lab2/Makefile4 :language: make :linenos: Notes: * A phony target is one that is not really the name of a file; rather it is just a name for a command to be executed. Here, the target `clean` is phony because it does not create a file named `clean`. * We execute this makefile using the following shell commands: .. code-block:: none $ make -f Makefile4 $ make -f Makefile4 clean Makefile5 ------------------ We can add more tasks (as phony targets) to the makefile: .. literalinclude:: ../hpsc2020/Labs/lab2/Makefile5 :language: make :linenos: We execute this using the follwoing shell commands: .. code-block:: none $ make -f Makefile5 $ make -f Makefile5 graph clean | **Further reading**: For further information see: https://www.gnu.org/software/make/manual/make.html __ http://math.unm.edu/~motamed/Teaching/Fall20/HPSC/fortran.html __ http://math.unm.edu/~motamed/Teaching/Fall20/HPSC/unix.html