The project provides a portable, versatile way of compiling Fortran, C, C++, and mixed projects.
cfortran.h can be used for Fortran-C interoperability.
Created November 2011 by Matthias Cuntz
while at the Department Computational Hydrosystems, Helmholtz Centre
for Environmental Research - UFZ, Permoserstr. 15, 04318 Leipzig,
Germany
and continued while at Institut National de Recherche en Agriculture,
Alimentation et Environnement (INRAE), Nancy, France.
It is distributed under the MIT License (see LICENSE file and below).
Copyright (c) 2011-2020 Matthias Cuntz - mc (at) macu (dot) de
Compiling a program from source code is an elaborate process. The compiler has to find all source files, of course. It has to know all dependencies between the source files. For C programs, it has to find all the header (.h) files. For Fortran programs, it has to find the module (.mod) files, which are produced by the compiler itself, which means that the files have to be compiled in a certain order. Last but not least, the compiler and linker have to find external libraries and use appropriate compiler options.
Different solutions exist for this problem, the two most prominent being GNU's configure and Kitware's CMake. One almost always has to give non-standard directories on the command line, e.g.
configure --with-netcdf=/path/to/netcdf
cmake -DCMAKE_NETCDF_DIR:STRING=/path/to/netcdf
Therefore, one has to know all installation directories, configure and cmake options, etc. for the current computer (system), or load the appropriate, matching modules, which is tedious if you or a team work on several computers such as your local computer for development and one or two clusters or supercomputers for production. This can be externalised in CMake by giving a script with -C or -P once all information was gathered.
This Makefile project follows a similar idea that the information about the current computer (system) must only be gathered once and stored in a config file. The user can then easily compile the same code on different computer (systems) with different compilers in debug or release mode, by simply telling on the command line, for example:
make system=mcinra compiler=gnu release=debug
This uses the system specific files mcinra.alias to look for the default GNU compiler, which is version 9.2 in this case and then uses all variables set in the file mcinra.gnu92. The user has to provide mcinra.alias and mcinra.gnu92 populated with the directories and specific compiler options for the GNU compiler suite 9.2 on the macOS system mcinra. Checking the same code with another compiler would be (given mcinra.intel* exists):
make system=mcinra compiler=intel release=debug
After checking with debug compiler options, one can simply compile the release version of the program by typing:
make system=mcinra compiler=intel release=release
Once mcinra.alias and mcinra.gnu92 are setup, they can be reused for every other project on the computer (system) mcinra.
The project includes examples for different operating systems, i.e. Unix (e.g. pearcey), Linux (e.g. explor), macOS (e.g. mcinra), and Windows (e.g. uwin). The system mcinra provides examples for different compilers, i.e. the GNU compiler suite, the Intel compiler suite, the NAG Fortran compiler, and the PGI Fortran compiler.
The project provides some standard configurations for the GNU compiler suite such as homebrew on macOS, ubuntu on Linux, and cygwin and ubuntu (uwin) on Windows.
The library is maintained with a git repository at:
https://github.com/mcuntz/jams_makefile/
To use it, checkout the git repository:
git clone https://github.com/mcuntz/jams_python.git
Open the file Makefile and make the proper settings for your source code (compiler, release, netcdf, openmp, mpi, lapack, etc.). Read the header of the Makefile for targets, etc. make info gives detailed information. Then run make:
make
You can give most makefile switches on the command line as well because they are simply variables, e.g.:
make system=mcair compiler=gnu netcdf=netcdf4
You might have to setup your computer (system) and compilers first (see below). You might also use some generic setups with the GNU compiler suite such as homebrew on macOS (everything in /usr/local), ubuntu on Linux (/usr), and cygwin and ubuntu (uwin) on Windows (/usr).
-
The makefile provides dependency generation using the Python script make.config/make.d.py. Dependency generation must be done in serial. Parallel make (-j) does hence not work from scratch.
One can split dependency generation and compilation by first calling make with a dummy target, which creates all dependencies, and then second calling parallel make with the -j switch, i.e.:make system=mcinra compiler=intel release=release dum make -j 8 system=mcinra compiler=intel release=release
-
The static switch is maintained like a red-headed stepchild. Libraries might be not ordered correctly if static linking and --begin-group/--end-group is not supported by the linker.
-
C- and C++-file dependencies are generated with:
$(CC) -E $(DEFINES) -MM
Mixed project with Fortran and C code using revision system git, and used on different computer systems. The Fortran code is in the subdirectory src/fortran and the C code in src/c.
-
Copy file Makefile and directory make.config into project home.
-
Edit Makefile giving the directories with the source files, a sensible PROGNAME, and setting appropriate libraries such as netcdf, openmp and/or mpi:
SRCPATH := src/fortran src/c PROGNAME := myproject netcdf := netcdf4 lapack := true
-
Debug project on computer with system with name, for example, mcinra:
make system=mcinra compiler=gnu release=debug && ./myproject
-
Added new use module, only: func in one of the Fortran source files: one has to re-generate dependencies first:
make system=mcinra compiler=gnu release=debug depend make system=mcinra compiler=gnu release=debug && ./myproject
-
Debug further with other compilers:
make system=mcinra compiler=intel release=debug && ./myproject make system=mcinra compiler=nag release=debug && ./myproject
-
Produce fast release version:
make system=mcinra compiler=intel release=release && ./myproject
-
Clean the project and commit everything to revision system (use target distclean to clean builds with several compilers or debug and release code; target clean removes only code from current compiler and build):
make system=mcinra compiler=intel release=release distclean git add Makefile make.config git commit -a -m "Debugged project with different compilers" && git push
-
Checkout the project on deployment machine, for example, explor:
git clone https://github.com/mcuntz/project.git cd project make system=explor compiler=intel release=release
-
Run the program.
qsub submit_myproject.sh
Note, for faster parallel builds, the above make command can be split in two (see Note 1), which is expedient for large code bases:
make system=explor compiler=intel release=release dum
make -j 8 system=explor compiler=intel release=release
As an example, one wants to add the PGI compiler suite version 19.10 on
the system eve.
Choose the compiler abbreviation pgi1910, i.e. leading to system=eve compiler=pgi1910.
- Create a config file in make.config/ with the name eve.pgi1910.
One can copy an existing file and adapt it to the new compiler.
Take a compiler close to the new one, e.g. eve.pgi83 or mcinra.pgfortran194.
If there is none, take explor.gnu63 on a Linux system and mcinra.gnu92 on macOS as good starting points. - Adapt the directories and compiler switches in the config file.
- If wanted, you can give aliases for that compiler, e.g. simply pgi instead of the name
including the version number pgi1910.
Edit eve.alias, follow the examples of pgfortran or intel.
As an example, one wants to port the Makefile onto a new system called Liclus on which exists a GNU compiler version 4.7, i.e. leading to system=liclus compiler=gnu47.
- Create a config file in make.config/ with the name liclus.gnu47.
One can copy an existing file of a similar system and a similar compiler.
For example if Liclus is macOS, then mcinra.gnu92 might be a good start and if Liclus is Linux then explor.gnu63 might give a head start. - Adapt the directories and compiler switches in the config file.
- One can give aliases for that compiler, e.g. simply gnu instead of the name
including the version number gnu47.
Therefore a file liclus.alias is needed. Create it in make.config/; even an empty one is possible if no aliases are wanted. One can copy explor.alias or mcinra.alias for examples.
This file is part of the JAMS Makefile system, distributed under the MIT License.
Copyright (c) 2011-2020 Matthias Cuntz - mc (at) macu (dot) de
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.