Commit 863d09ec authored by Robert Schweppe's avatar Robert Schweppe
Browse files

Merge remote-tracking branch 'remotes/origin/develop' into...

Merge remote-tracking branch 'remotes/origin/develop' into 71-integrate-auxiliary-cmake-repositories
parents 5f5b0d75 9cd796aa
......@@ -8,6 +8,7 @@ stages:
- info
- build
- test
- coverage
- deploy
workflow:
......@@ -40,13 +41,19 @@ show-env-vars:
- mkdir -p $BUILD_DIR
- cd $BUILD_DIR
# make command
.cmake: &cmake
- cmake .. $CMAKE_FLAGS
- echo -e "${PATH}"
- which python
# make command
.make: &make
- make $MAKE_FLAGS
# make test command
.make_test: &make_test
- make $MAKE_FLAGS test
- ctest -V
# make test command
.make_coverage: &make_coverage
......@@ -91,7 +98,7 @@ show-env-vars:
- source $MODULE_LOAD_SCRIPT
- python -m src_python.pre_proc.update_tfs_in_fortran_source
- *setup_build_dir
- cmake $CMAKE_FLAGS ..
- *cmake
- *make
- *cleanup
......@@ -101,7 +108,7 @@ show-env-vars:
script:
- source $MODULE_LOAD_SCRIPT
- *setup_build_dir
- cmake $CMAKE_FLAGS ..
- *cmake
- *make
- *make_test
- *cleanup
......@@ -129,11 +136,11 @@ show-env-vars:
# template for coverage jobs
.coverage_template: &coverage_template
stage: test
stage: coverage
script:
- source $MODULE_LOAD_SCRIPT
- *setup_build_dir
- cmake $CMAKE_FLAGS ..
- *cmake
- *make
- *make_coverage
- *cleanup
......@@ -367,7 +374,6 @@ build-intel19MPI-release:
valgrind-gfortran64MPI-debug:
needs:
- job: build-gfortran64MPI-debug
artifacts: true
variables:
<<: *debug_vars
MODULE_LOAD_SCRIPT: moduleLoadScripts/eve.gfortran64MPI
......@@ -376,7 +382,6 @@ valgrind-gfortran64MPI-debug:
valgrind-gfortran73MPI-debug:
needs:
- job: build-gfortran73MPI-debug
artifacts: true
variables:
<<: *debug_vars
MODULE_LOAD_SCRIPT: moduleLoadScripts/eve.gfortran73MPI
......@@ -385,7 +390,6 @@ valgrind-gfortran73MPI-debug:
valgrind-gfortran83-debug:
needs:
- job: build-gfortran83-debug
artifacts: true
variables:
<<: *debug_vars
MODULE_LOAD_SCRIPT: moduleLoadScripts/eve.gfortran83
......@@ -410,12 +414,14 @@ valgrind-gfortran83-debug:
# MODULE_LOAD_SCRIPT: moduleLoadScripts/eve.nagfor62
# <<: *valgrind_template
# #################
# ### TEST JOBS ###
# #################
#################
### TEST JOBS ###
#################
# NAG fails on lightweight_lib
test-nag62-debug:
needs:
- job: build-nag62-debug
artifacts: false
variables:
<<: *debug_vars
MODULE_LOAD_SCRIPT: moduleLoadScripts/eve.nagfor62
......@@ -423,6 +429,9 @@ test-nag62-debug:
<<: *test_template
test-nag62-release:
needs:
- job: build-nag62-release
artifacts: false
variables:
<<: *release_vars
MODULE_LOAD_SCRIPT: moduleLoadScripts/eve.nagfor62
......@@ -432,6 +441,7 @@ test-nag62-release:
test-gfortran83MPI-release:
needs:
- job: build-gfortran83MPI-release
artifacts: false
variables:
<<: *release_vars
MODULE_LOAD_SCRIPT: moduleLoadScripts/eve.gfortran83MPI
......@@ -441,15 +451,27 @@ test-gfortran83MPI-release:
test-gfortran73MPI-release:
needs:
- job: build-gfortran73MPI-release
artifacts: false
variables:
<<: *release_vars
MODULE_LOAD_SCRIPT: moduleLoadScripts/eve.gfortran73MPI
CMAKE_FLAGS: '-DCMAKE_BUILD_TYPE=Release'
<<: *test_template
test-gfortran73MPI-debug:
needs:
- job: build-gfortran73MPI-debug
artifacts: false
variables:
<<: *debug_vars
MODULE_LOAD_SCRIPT: moduleLoadScripts/eve.gfortran73MPI
CMAKE_FLAGS: '-DCMAKE_BUILD_TYPE=Debug'
<<: *test_template
test-gfortran83-release:
needs:
- job: build-gfortran83-release
artifacts: false
variables:
<<: *release_vars
MODULE_LOAD_SCRIPT: moduleLoadScripts/eve.gfortran83
......@@ -457,6 +479,9 @@ test-gfortran83-release:
<<: *test_template
test-intel18-release:
needs:
- job: build-intel18-release
artifacts: false
variables:
<<: *release_vars
MODULE_LOAD_SCRIPT: moduleLoadScripts/eve.intel18
......@@ -464,6 +489,9 @@ test-intel18-release:
<<: *test_template
test-intel19-release:
needs:
- job: build-intel19-release
artifacts: false
variables:
<<: *release_vars
MODULE_LOAD_SCRIPT: moduleLoadScripts/eve.intel19
......@@ -471,6 +499,9 @@ test-intel19-release:
<<: *test_template
test-intel19MPI-release:
needs:
- job: build-intel19MPI-release
artifacts: false
variables:
<<: *release_vars
MODULE_LOAD_SCRIPT: moduleLoadScripts/eve.intel19MPI
......@@ -482,6 +513,8 @@ test-intel19MPI-release:
# ###################
coverage-gcc73:
needs:
- job: test-gfortran73MPI-debug
variables:
<<: *debug_vars
<<: *coverage_vars
......
cmake_minimum_required(VERSION 3.12)
cmake_minimum_required(VERSION 3.14)
# check version file
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/version.txt")
file(STRINGS "version.txt" MPRVERSION LIMIT_COUNT 1)
else()
set(MPRVERSION "0.0.0-dev0") # default version
endif()
# version should be of the form (semver.org):
# - 1.2.3-dev0 (development with number)
# - 1.2.3-rc1 (release candidate with number)
# - 1.2.3 (release)
# remove possible "v" prefix and find major.minor.patch version
string(REGEX MATCH "^v?([0-9]+)" _ ${MPRVERSION})
set(ver_major ${CMAKE_MATCH_1})
string(REGEX MATCH "^v?[0-9]+\.([0-9]+)" _ ${MPRVERSION})
set(ver_minor ${CMAKE_MATCH_1})
string(REGEX MATCH "^v?[0-9]+\.[0-9]+\.([0-9]+)" _ ${MPRVERSION})
set(ver_patch ${CMAKE_MATCH_1})
# find pre-release tag
string(REGEX MATCH ".*-(.+)" _ ${MPRVERSION})
set(ver_pre ${CMAKE_MATCH_1})
# create the version string for cmake (fill up with 0)
if ("${ver_major}" STREQUAL "")
set(ver_major 0) # default version
endif()
if ("${ver_minor}" STREQUAL "")
set(ver_minor 0) # default version
endif()
if ("${ver_patch}" STREQUAL "")
set(ver_patch 0) # default version
endif()
set(ver_final ${ver_major}.${ver_minor}.${ver_patch}) # full version
# check date file (if not a development version)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/version_date.txt" AND (NOT (${ver_pre} MATCHES "^dev.*")))
file(STRINGS "version_date.txt" MPRDATE LIMIT_COUNT 1)
else()
string(TIMESTAMP MPRDATE "%Y-%m-%d") # current date
endif()
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules)
# get version and date from files (version.txt and version_date.txt)
include(version)
get_version(MPR_VER MPR_VER_DEV MPR_DATE)
# create the project
project(MPR
VERSION ${ver_final}
VERSION ${MPR_VER}
DESCRIPTION "Multiscale Parameter Regionalization"
HOMEPAGE_URL "https://www.ufz.de/index.php?en=40126"
LANGUAGES Fortran)
message("MPR VERSION: ${MPR_VERSION} (from ${MPRVERSION})")
message("MPR DATE: ${MPRDATE}")
# add full version and date to pre-processor flags (qoutes need in before hand)
add_compile_definitions(MPRVERSION='${MPR_VER_DEV}' MPRDATE='${MPR_DATE}')
# common (default) options to cmake
# -DCMAKE_BUILD_TYPE=Release - compile in debug or release mode
......@@ -59,59 +25,16 @@ message("MPR DATE: ${MPRDATE}")
# automatically enables testing
include(CTest)
# turn off testing in case the Project is not the main
if(NOT CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
set(BUILD_TESTING OFF)
endif()
# add version to pre-processor flags (qoutes need in before hand)
add_compile_definitions(MPRVERSION='${MPRVERSION}')
# add date to pre-processor flags (qoutes need in before hand)
add_compile_definitions(MPRDATE='${MPRDATE}')
# The variable "CMAKE_BUILD_MODULE_SYSTEM_INDEPENDENT" can be set before executing cmake via a cache command:
# $cmake -DCMAKE_BUILD_MODULE_SYSTEM_INDEPENDENT:STRING=ON ..
# or cache file:
# $cmake -C ../CMakeCacheFiles/eve ..
# or after executing CMake editing the CMakeCache.txt, preferably with a corresponding cmake editor i.e ccmake
set(CMAKE_BUILD_MODULE_SYSTEM_INDEPENDENT OFF CACHE STRING "build the module INDEPENDENT of the module system,
so the build in the build tree works even after a module purge")
message(STATUS "build INDEPENDENT of module system ${CMAKE_BUILD_MODULE_SYSTEM_INDEPENDENT}")
option(CMAKE_BUILD_MODULE_SYSTEM_INDEPENDENT "build the module INDEPENDENT of the module system, so the build in the build tree works even after a module purge")
# same with coverage
set(CMAKE_WITH_COVERAGE OFF CACHE STRING "build the module with gcov coverage support")
# additional cmake-modules created for the purpose of finding external libs in the source_directory in
# a folder named cmake-modules. This command tells cmake to search there for Find<module>.cmake files
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules)
# set compiling flags for debug and relese version
if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU")
# https://www.mankier.com/1/gfortran#Options-Options_to_request_or_suppress_errors_and_warnings
# this is super detailed...
# set(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -Og -g -Wall -Wextra -Warray-temporaries -Wconversion -Wrealloc-lhs-all -fimplicit-none -fbacktrace -fcheck=all -ffpe-trap=zero,overflow,underflow -finit-real=snan")
set(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -Og -g -Wall -Wextra -fimplicit-none -fbacktrace -fcheck=all -ffpe-trap=zero,overflow,underflow -finit-real=snan")
set(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE} -Ofast")
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -ffree-line-length-none")
endif()
if(CMAKE_Fortran_COMPILER_ID MATCHES "Intel")
set(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -warn all -g -debug extended -traceback -fp-stack-check -O0 -check all -fstack-protector-all -fstack-security-check -fpe0")
set(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE} -O3 -qoverride-limits")
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -assume byterecl -fp-model=source -m64 -assume realloc_lhs")
endif()
if(CMAKE_Fortran_COMPILER_ID MATCHES "NAG")
set(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -g -nan -O0 -C=all -strict95 -ieee=stop")
# set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pedantic -Wall -W -O -g")
set(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE} -O4 -ieee=full")
# set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3")
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -colour -unsharedf95 -ideclient -fpp")
endif()
if(CMAKE_Fortran_COMPILER_ID MATCHES "PGI")
set(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -C -c -g -traceback -O0")
set(CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE} -fast")
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -Mfree -mcmodel=medium")
endif()
option(CMAKE_WITH_COVERAGE "build the module with gcov coverage support")
# library module specific settings
add_subdirectory(./src)
......
# get_version : get version information form files
#
# version.txt - version string
# version_date.txt - version date sting
#
# get_version(VER VER_DEV DATE)
#
# will store version string in "VER" and version date string in "DATE"
function(get_version ver_short ver_full ver_date)
# check version file
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/version.txt")
file(STRINGS "version.txt" ver_file LIMIT_COUNT 1)
else()
set(ver_file "0.0.0-dev0") # default version
endif()
# version should be of the form (semver.org):
# - 1.2.3-dev0 (development with number)
# - 1.2.3-rc1 (release candidate with number)
# - 1.2.3 (release)
# remove possible "v" prefix and find major.minor.patch version
string(REGEX MATCH "^v?([0-9]+)" _ ${ver_file})
set(ver_major ${CMAKE_MATCH_1})
string(REGEX MATCH "^v?[0-9]+\.([0-9]+)" _ ${ver_file})
set(ver_minor ${CMAKE_MATCH_1})
string(REGEX MATCH "^v?[0-9]+\.[0-9]+\.([0-9]+)" _ ${ver_file})
set(ver_patch ${CMAKE_MATCH_1})
# find pre-release tag
string(REGEX MATCH ".*-(.+)" _ ${ver_file})
set(ver_pre ${CMAKE_MATCH_1})
# create the version string for cmake (fill up with 0)
if ("${ver_major}" STREQUAL "")
set(ver_major 0) # default version
endif()
if ("${ver_minor}" STREQUAL "")
set(ver_minor 0) # default version
endif()
if ("${ver_patch}" STREQUAL "")
set(ver_patch 0) # default version
endif()
# create version x.y.z
set(ver ${ver_major}.${ver_minor}.${ver_patch})
# check date file (if not a development version)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/version_date.txt" AND (NOT (${ver_pre} MATCHES "^dev.*")))
file(STRINGS "version_date.txt" date LIMIT_COUNT 1)
else()
string(TIMESTAMP date "%Y-%m-%d") # current date
endif()
message(STATUS "use VERSION: ${ver} (from ${ver_file})")
message(STATUS "use DATE: ${date}")
# set given variables in parent scope
set(${ver_date} ${date} PARENT_SCOPE)
set(${ver_full} ${ver_file} PARENT_SCOPE)
set(${ver_short} ${ver} PARENT_SCOPE)
endfunction()
.fuse_hidden*
*.o
*.so
test
test_mpi
*.mod
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = https://github.com/gitporst/flogging.git
branch = mpr_compat
commit = 789047189a2e7cedd78272522cfeedc9f1163faa
parent = 3ea27226b2ee091e3009ce6002d5e5b31d1792ac
method = merge
cmdver = 0.4.0
Copyright (c) 2016 Daan van Vugt
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.
# Builds flogging as a shared library
F90=.f90
OBJ=.o
LIB = libflogging.so
SRC = src
BUILD = $(SRC)
FC = mpif90
ifeq ($(FC),nagfor)
FLAGS = -fpp -colour -free -Iinclude -DNAG
LIB_FLAGS =
else
FLAGS = -fpic -O3 -cpp -ffree-line-length-none -Iinclude
LIB_FLAGS = -shared
endif
ifeq (USE_MPI,1)
FLAGS += -DUSE_MPI
endif
SOURCES = $(wildcard $(SRC)/*$(F90))
OBJS = $(patsubst $(SRC)/%$(F90), $(BUILD)/%$(OBJ), $(SOURCES))
$(LIB): $(OBJS)
$(FC) $(LIB_FLAGS) -o $(LIB) $(OBJS)
$(BUILD)/%$(OBJ): $(SRC)/%$(F90)
$(FC) $(FLAGS) -c $< -o $@
$(BUILD)/flogging.o: $(BUILD)/vt100.o
# Some test executables
#
test : src/flogging.f90 src/tests/test_flogging.f90 src/vt100.f90 include/flogging.h
$(FC) $(FLAGS) -Iinclude src/vt100.f90 src/flogging.f90 src/tests/test_flogging.f90 -o test
test_mpi : src/flogging.f90 src/tests/test_flogging.f90 src/vt100.f90 include/flogging.h
$(FC) $(FLAGS) -DUSE_MPI -Iinclude src/vt100.f90 src/flogging.f90 src/tests/test_flogging.f90 -o test_mpi
.PHONY: clean doc
clean:
@find . -name '*.o' -delete
@find . -name '*.mod' -delete
@find . -name '*.so' -delete
@rm -f test test_mpi
doc:
ford flogging.md
doc_deploy: doc
git subtree push --prefix doc origin gh-pages
# Flogging
This logging system aims to be simple to use, similar to existing write statements and therefore easy to implement in existing codes,
while providing some convenient extra features not present in fortran by default.
Usage is very simple:
```fortran
use flogging
```
To load the logging module and its utility functions.
To use the preprovided macros, include the header file and turn on the preprocessor by compiling with `-cpp`
```C
#include "flogging.h"
```
This macro defines the following functions:
```fortran
log_fatal(format)
log_error(format)
log_warn(format)
log_info(format)
log_debug(format)
log_trace(format)
log_root_fatal(format)
log_root_error(format)
log_root_warn(format)
log_root_info(format)
log_root_debug(format)
log_root_trace(format)
```
Where `format` is usually `*`, but can be adjusted to suit your needs. Remember to include a single `A` specifier for the log lead if you set it yourself.
The functions print a log message only if the level is greater than or equal to the current minimum loglevel.
The second functions include a check to print a log message only from the root MPI process, if compiled
with `USE_MPI`. If not compiled with `USE_MPI`, it works the same as the log function.
If you do not want to use the preprocessor macros, you can log like this.
```fortran
if (logp(LEVEL)) then
write(logu,FORMAT) trim(logl(LEVEL))//" ", "this is a log message"
endif
```
Note that it is not possible to use the filename and linenumber features then.
# Installation
You can compile `flogging.f90` and `vt100.f90` into a library `flogging.so` with `make`.
To compile with MPI support use
```bash
make USE_MPI=1
```
Then, place `libflogging.so` into your `LD_LIBRARY_PATH` and put `flogging.h` into your include path.
## Compilation flags
Compile your own code with
```
-DDISABLE_LOG_DEBUG
```
to remove any debug statements from your code. Note that you cannot use `-v` to get these back then.
By default any `log_trace` messages are not included in the executable. Compile with
```
-DENABLE_LOG_TRACE
```
to get these back. Note that you still need to use `-vv` (by default) to see the messages.
This is a good compilation flag to have in your debug build profile.
## Transitioning to flogging
You can change all of the messages in your application to the `log_info` level by
```bash
git sed 's/write ?(\*,\*)/log_info(*)/g'
```
You might need to split some lines, as the `__FILE__` and `__LINE__` macros require quite some space.
This can also be circumvented by compiling with `-ffree-line-length-none` or equivalent.
# Examples
```fortran
log_error(*) "this is an error"
log_warn(*) "this is a warning"
log_info(*) "here, have some info"
log_debug(*) "and this is a debug message"
```
outputs (without any compilation flags)
```
localhost test.f90:9 ERROR this is an error
localhost test.f90:10 WARN this is a warning
localhost test.f90:11 INFO here, have some info
```
(The leading space is a consequence of using the fortran * format specifier)
## Interface
The module `flogging` defines some subroutines you can use to alter it's behaviour:
```fortran
public :: log_set_output_hostname
public :: log_set_output_severity
public :: log_set_output_date
public :: log_set_time_only
public :: log_set_output_fileline
public :: log_set_skip_terminal_check
public :: log_set_disable_colors
public :: log_disable_cli_arguments
```
## Command-line flags
The logging module allows the following command-line flags, checked at the time of the first log output.
```
-v, --verbose Increase the logging verbosity
-q, --quiet Decrease the logging verbosity
--log-output-hostname Output the hostname in the log lead
--log-force-colors Force colors, even when outputting to a file
--log-no-colors Disable colors (overrides --log-force-colors)
--log-output-date Output the date in the log lead
--log-output-time Output the time in the log lead
```
### Argument parsing
By default, flogging parses the command-line arguments above on the first invocation of `log()`.
If you want to use your own argument parsing you can disable this behaviour by calling `log_disable_cli_arguments` before any output is logged.
## Output to a file
Logging is to stderr by default. Another output unit can be selected by setting
```fortran
logu = some_unit
```
This will probably not work well with MPI.
## Internals
The latest documentation can be found [here](http://exteris.github.io/flogging/).
The module defines a function defining whether or not to print this log message,
```fortran
logp(level)
```
where level is one of `LOG_FATAL`, `LOG_ERROR`, `LOG_WARN`, `LOG_INFO`, `LOG_DEBUG` or `LOG_TRACE`.
The logp function can also be used with MPI support, using an optional integer parameter `only_n` to return true
only if the level is sufficient and the MPI rank of the current thread is equal to `only_n`.
This can be used to print messages only from the root thread, to prevent log messages from being printed N times as shown below.
```fortran
logp(level,0)
```
## Contributing
Pull requests and comments are appreciated.
## TODO
- Argument parsing is quite flaky (try -qqqqqq and see what happens)
- Time and date output at the same time does not work yet
- Compilation flag to control argument parsing
- Log output to a different file per MPI process
- Multiple logging streams/outputs (this will be tricky)
project: flogging
project_github: https://github.com/exteris/flogging
summary: FLOGGING -- Fortran logging system with MPI support
author: Daan van Vugt
github: https://github.com/exteris
project_dir: ./src
output_dir: ./doc
docmark: <
docmark_alt: *
predocmark: >
predocmark_alt: #
graph: true
coloured_edges: true
This library provides a logging framework for Fortran 90 and up, with MPI support, vt100 colors, command-line arguments and more.
License
---------------
The bspline-fortran source code and related files and documentation are distributed under a permissive [license](https://github.com/exteris/flogging/blob/master/LICENSE) (MIT).
/* This code is governed by the MIT license. See LICENSE for details. */