This repository provides the skeleton of a Fortran project. It can be used as a template when beginning a new piece of Fortran software.
To use this template, run the following commands:
git clone https://git.ccfe.ac.uk/soft-eng-group/rse/skeleton-repositories/fortran.git PROJECTNAME
cd PROJECTNAME
rm -Rf .git/
git init
This will download the files onto your computer and create a new git repository. You will also need to install the following software (needed during the build and testing process) on your computer: gfortran, cmake, graphviz, FORD, and gcovr. On Ubuntu this can be achieved by running
sudo apt-get install python-pip gfortran cmake graphviz gcovr
sudo pip install ford
Finally, you will need to build the pFUnit framework on your computer so you can run unit tests. This should be done using CMake, adapting installation directory and other configurations as necessary:
pfdir=$HOME/.pfunit
wget https://downloads.sourceforge.net/project/pfunit/Source/pFUnit-3.2.9.tgz
tar xvf pFUnit-3.2.9.tgz
mkdir pFUnit-3.2.9/build
cd pFUnit-3.2.9/build
cmake -DMPI=NO -DOPENMP=NO -DCMAKE_INSTALL_PREFIX=$pfdir -DINSTALL_PATH=$pfdir ..
make
make install
echo "export CMAKE_PREFIX_PATH=$pfdir:$CMAKE_PREFIX_PATH" >> $HOME/.bashrc
. $HOME/.bashrc
You should then do the following to customise your new project:
- Change the copyright header in each file, specifying the project name, the names of the authors, and the year(s) of releases.
- Edit LICENSE.md and file headers to contain your preferred software license.
- Edit CONTRIBUTING.md to explain the workflow for your project, along with any other information which would be needed by new developers.
- Change source file names/directory structure according to your needs.
- Edit the various CMakeLists.txt files according to how you wish to structure the libraries and/or executables in your project.
- Change the compiler flags specified in SetFortranFlags.cmake to meet your needs.
- If using MPI, configure cmake to use the appropriate compiler wrappers.
- Edit the contents of ford-docs.md and the doc-pages directory to provide documentation for your project.
- Replace the contents of this README with a description of your own project.
- Commit your changes and push your new repository to GitLab.
- Set up various configurations in the GitLab and consider making a
wiki. Under Settings > CI/CD > General pipelines > Test coverage
parsing, specify the regular expression
^lines: (\d+.\d+)%
. - Set badges for your project under Settings > General > Badges, using the ones in this README as an example.
Some basic configurations for CMake are provided. These are (heavily) adapted from the template provided by Seth Morton on GitHub. The build system was refactored to comply with modern CMake best practice, adapted slightly to account for differences between Fortran and C++.
In particular, Fortran presents the challenge of indicating which modules in a library should be publicly accessible to an end user and which should be for internal use only. This was achieved by making the private modules part of a separate target from the overall library. This target was a static library which was then used as a private dependency by the main (publicly accessible) library. This ensured that CMake would keep the public and private module interfaces separate and not expose the latter to the end user. See src/project_lib/CMakeLists.txt.
The basic approach to building with CMake is as follows:
mkdir build; cd build
cmake ..
make
make install # If desired
Configurations can be specified as arguments to the cmake
command. In particular, you can specify -DCMAKE_BUILD_TYPE=<VALUE>
where value can be DEBUG
, TESTING
, or RELEASE
, with each one
changing compiler flags to be most suitable to the given use
case. DEBUG
builds will not be optimised, provides debugger symbols,
and performs additional run-time checks. TESTING
builds are not
optimised and have debugger symbols, but does not perform run-time
checks. Additionally, the binaries are instrumented to emit coverage
information describing which lines of source files have been
executed. Finally RELEASE
provides full optimisation (it is worth
experimenting with these settings, as the most aggressive optimisation
is not always the fastest) and does not offer any run-time checks,
debugger information, or instrumentation. If you wish to install to a
non-standard location, you can specify
-DCMAKE_INSTALL_PREFIX=<INSTALL-LOCATION>
when invoking CMake.
An example of using pFUnit for unit testing is provided in the
tests directory. pFUnit files (ending in .pf) are Fortran
files with a few additional
directives
included to indicate which routines implement tests and to provide
assertion testing. These are preprocessed before being passed to a
Fortran compiler. All of this is handled by the CMake configurations
in this directory; all you need to do is create additional files
ending in .pf
and specify in
tests/CMakeLists.txt any libraries which your
tests will need to link against. The tests will then automatically be
built when invoking make all
.
The tests can then either be run directly by executing
tests/unit_tests
from within the build directory or by running
ctest
from there. The latter will also run any additional tests
which you have configured in your
CMakeLists.txt
files. As an example, a simple integration test called
executable_test
was configured here. This simply runs the executable
produced when building this project and checking the format of the
output.
You should write unit tests which cover as much of your code as
possible (ideally approaching 100%). The gcovr
tool can be used to analyse this,
providing both an overall coverage percentage and a line-by-line
report. CMake has been configured to handle any tricky details
involved in this. To generate coverage reports for your unit tests,
run make gcovr
from withing the build directory after having run
them.
If it is to be useful to anyone else, you must document your code. Broadly speaking, you can consider three types of documentation:
- detailed comments within your code explaining the algorithm and implementation details
- API documentation describing the derived types, functions, subroutines, etc. within your code
- tutorials on installing and using the software
The first of these is a matter of programming style and you should provide clear guidelines to developers on how to do this.
While it is possible to manually write API documentation, this is
tedious and has the potential to become out of sync with your code. It
is generally considered that the best approach is to use an automatic
documentation
generator. Such
a tool will analyse your source code to determine the API and extract
specially-formatted comments explaining the purpose of each derived
type, function, etc. The best and most comprehensive such tool for
Fortran is FORD
and all example source files within this project contain comments
readable by FORD. You should add these to your own code as you write
it. This ensures that both users and developers will understand its
purpose. FORD reads its configurations, plus an overall description of
the code, from a project file, in this case called
ford-docs.md. CMake has been configured to be able to
build the documentation for you. Simply run make docs
in the build
directory. As described below, this project
is configured so that GitLab's continuous integration service will
generate and deploy this documentation (see an example
here).
You should write tutorials explaining how to use your software as well. Depending on the software in question it may also be useful to provide information on the physics and/or numerical methods involved. It can also be helpful to developers to provide a high-level overview of how the code is structured. This documentation could be provided in your project's GitLab wiki or as pages within your FORD documentation. A template for these sorts of pages is provided in the doc-pages directory.
GitLab offers a Continuous Integration service which can automatically build, test, and deploy your software. The results of trying to build and test your software are then displayed in the GitLab interface for each branch and merge request. The settings for this are provided in the .gitlab-ci.yml file. In this project, the CI configurations do the following:
- compile your code
- try to build API documentation
- run unit tests and produce coverage reports
- run any other tests and produce coverage reports
- check that cmake has been configured to install the project correctly
- if on the
master
branch, deploy documentation and code coverage to GitLab pages
These configurations mostly rely on CMake to do the hard work and therefore shouldn't need to be edited very often. However, it may be necessary to, e.g., install any third-party libraries which your project depends on. This can be done withing the configuration scripts or by creating a custom docker image to use when running the CI. Currently, a docker image is used which provide all of the tools necessary to build and test this project, plus some commonly used libraries such as MPICH, BLAS, and LAPACK.
Unfortunately, the current version of GitLab is not able to host pages for projects within sub-groups, such as this one. As such, the automatic deployment and hosting of documentation and code coverage reports can not be easily demonstrated. See a fork of this repository for a demonstration of this feature in action for documentation, unit test coverage, and coverage for all tests.
The texts of the LGPL and GPL (version 3) are included in this template and should not be deleted if you wish to use this license.
The GNU Lesser General Public License (LGPL) allows anyone to use and modify your software providing the derivatives are similarly licensed. However, unlike the stronger GNU General Public License (GPL), it still allows unmodified libraries to be used by proprietary or otherwise differently licensed software. For more on the different free and open source software licenses available, see choosealicense.com.
Please follow your own organisation's guidance on the choice of a license.
Notifications of licensing are provided at the top of all example source files. See the GNU project for more details on using their licenses. However, the inclusion of (L)GPL documents in this repository is for the purposes of a providing an example only. The repository itself is not licensed under the LGPL. It is licensed under the CC0 1.0, meaning it is effectively public domain.