Skip to content

1. A first build ​

Starting from scratch ​

fobis new writes a project that builds and tests at once: a module, a program, a test, a fobos with a debug and a release mode, a .gitignore and a git repository (nothing committed). It never overwrites a file.

console
$ cd ~ && fobis new demo
Created project demo in demo/ (git: initialised)
  src/demo.f90
  app/main.f90
  test/test_demo.f90
  fobos
  .gitignore
Next: cd demo && fobis build && fobis test
console
$ cd ~/demo && find . -type f -not -path "./.git/*" | sort
./.gitignore
./app/main.f90
./fobos
./src/demo.f90
./test/test_demo.f90
console
$ cd ~/demo && fobis build --quiet && ./build/debug/demo && fobis test --quiet

Building app/main.f90
Compiling src/demo.f90 serially
Compiling app/main.f90 serially
Linking build/debug/demo
Target app/main.f90 has been successfully built
mean of 1, 2, 3, 4:  2.5

Building test suite (1 target(s))...

  PASS  test_demo                      (0.00 s)

  Results: 1 passed, 0 failed, 0 skipped
  Total:   0.00 s

--lib makes a library (no program, static and shared library modes), fobis new . fills the current directory, --scaffold adds CI, documentation and release scripts. Reference: new.

The rest of this track builds quad, a project written by hand, to show what FoBiS does with no configuration and how a fobos grows from there.

The quad project ​

quad is a small library of quadrature rules, and a program that uses it. It has no build script: FoBiS reads the sources and works out the rest.

console
$ find . -type f | sort
./src/integrands.F90
./src/integrate.F90
./src/quad_rules.F90
fortran
!< Composite quadrature rules on a uniform grid.
module quad_rules
!< Composite quadrature rules on a uniform grid.
use, intrinsic :: iso_fortran_env, only : R8P => real64
implicit none
private
public :: R8P, integrand, trapezoid, simpson

abstract interface
   pure function integrand(x) result(y)
   !< A function of one real variable.
   import :: R8P
   real(R8P), intent(in) :: x !< Abscissa.
   real(R8P)             :: y !< Value at x.
   endfunction integrand
endinterface

contains
   pure function trapezoid(f, a, b, n) result(s)
   !< Composite trapezoidal rule with n intervals.
   procedure(integrand)  :: f !< Integrand.
   real(R8P), intent(in) :: a !< Lower bound.
   real(R8P), intent(in) :: b !< Upper bound.
   integer,   intent(in) :: n !< Number of intervals.
   real(R8P)             :: s !< Integral estimate.
   real(R8P)             :: h !< Interval width.
   integer               :: i !< Counter.

   h = (b - a) / n
   s = 0.5_R8P * (f(a) + f(b))
   do i = 1, n - 1
      s = s + f(a + i * h)
   enddo
   s = s * h
   endfunction trapezoid

   pure function simpson(f, a, b, n) result(s)
   !< Composite Simpson rule with n intervals (n even).
   procedure(integrand)  :: f !< Integrand.
   real(R8P), intent(in) :: a !< Lower bound.
   real(R8P), intent(in) :: b !< Upper bound.
   integer,   intent(in) :: n !< Number of intervals, even.
   real(R8P)             :: s !< Integral estimate.
   real(R8P)             :: h !< Interval width.
   integer               :: i !< Counter.

   h = (b - a) / n
   s = f(a) + f(b)
   do i = 1, n - 1
      s = s + merge(4._R8P, 2._R8P, mod(i, 2) == 1) * f(a + i * h)
   enddo
   s = s * h / 3._R8P
   endfunction simpson
endmodule quad_rules
fortran
!< Functions to integrate.
module integrands
!< Functions to integrate.
use quad_rules, only : R8P
implicit none
private
public :: sine

contains
   pure function sine(x) result(y)
   !< sin(x).
   real(R8P), intent(in) :: x !< Abscissa.
   real(R8P)             :: y !< sin(x).

   y = sin(x)
   endfunction sine
endmodule integrands
fortran
!< Integrate sin(x) over [0, pi], whose exact value is 2, with more and more intervals.
program integrate
!< Integrate sin(x) over [0, pi], whose exact value is 2, with more and more intervals.
use quad_rules, only : R8P, trapezoid, simpson
use integrands, only : sine
implicit none
real(R8P), parameter :: pi = acos(-1._R8P) !< Pi.
integer              :: n                  !< Number of intervals.

print '(A6, 2A18)', 'n', 'trapezoid error', 'simpson error'
n = 4
do while (n <= 64)
   print '(I6, 2ES18.4)', n, abs(trapezoid(sine, 0._R8P, pi, n) - 2._R8P), abs(simpson(sine, 0._R8P, pi, n) - 2._R8P)
   n = 2 * n
enddo
endprogram integrate

The program uses two modules, and integrands uses quad_rules: that order must be respected by whatever compiles them, because a module has to be compiled before the files that use it.

Building ​

console
$ fobis build
[auto-discover] src not set; scanning: ./src
Builder options
  Directories
    Root of source files directory: "src"
    Building directory: "."
    Compiled-objects .o   directory: "obj"
    Compiled-objects .mod directory: "mod"
  Compiler options
    Vendor: "gnu"
    Compiler command: "gfortran"
    Module directory switch: "-J"
    Compiling flags: "-c"
    Linking flags: ""
    Preprocessing flags: ""
    Coverage: False
    Profile: False
  Archiver: ar -rcs
  Ranlib: ranlib
  Preprocessor used: None
  Preprocessor args: 
  Preprocessor output directory: None
  Preprocessor extensions processed: []

Building src/integrate.F90
Compiling src/quad_rules.F90 serially
Compiling src/integrands.F90 serially
Compiling src/integrate.F90 serially
Linking ./integrate
Target src/integrate.F90 has been successfully built
  • With no configuration, FoBiS looks for the conventional source directories (src/, then source/, then app/) and scans the first one it finds; without any of them it scans the current directory.
  • Builder options is the configuration in use: the default compiler (gnu, that is gfortran), flags, and where objects (obj/) and module files (mod/) go.
  • Every program found is a target. FoBiS parsed each file for module, use, include and program statements and compiled quad_rules, then integrands, then the program, whatever the file names.

The build left the objects, the module files and the executable, named after the program's file:

console
$ find . -type f | sort
./integrate
./mod/integrands.mod
./mod/quad_rules.mod
./obj/.fobis-compiler.json
./obj/integrands.o
./obj/integrate.o
./obj/quad_rules.o
./src/integrands.F90
./src/integrate.F90
./src/quad_rules.F90

obj/.fobis-compiler.json records which compiler built the objects: when it changes (another gfortran after a module load, for example), the next build recompiles everything instead of mixing objects of two compilers.

console
$ ./integrate
     n   trapezoid error     simpson error
     4        1.0388E-01        4.5598E-03
     8        2.5768E-02        2.6917E-04
    16        6.4297E-03        1.6591E-05
    32        1.6066E-03        1.0334E-06
    64        4.0161E-04        6.4530E-08

Both errors fall as the number of intervals grows; how fast is the subject of a test in chapter 4.

fobis build with no fobos, then fobis clean

Recorded on a small demo project, not quad.

Rebuilding ​

FoBiS compiles only what is out of date. Run again, nothing is compiled:

console
$ fobis build
[auto-discover] src not set; scanning: ./src
Builder options
  Directories
    Root of source files directory: "src"
    Building directory: "."
    Compiled-objects .o   directory: "obj"
    Compiled-objects .mod directory: "mod"
  Compiler options
    Vendor: "gnu"
    Compiler command: "gfortran"
    Module directory switch: "-J"
    Compiling flags: "-c"
    Linking flags: ""
    Preprocessing flags: ""
    Coverage: False
    Profile: False
  Archiver: ar -rcs
  Ranlib: ranlib
  Preprocessor used: None
  Preprocessor args: 
  Preprocessor output directory: None
  Preprocessor extensions processed: []

Building src/integrate.F90
Nothing to compile, all objects are up-to-date
Linking ./integrate
Target src/integrate.F90 has been successfully built

Change quad_rules.F90 (here touch stands for an edit) and everything that depends on it is recompiled, through the chain of use statements; --quiet drops the options block:

console
$ touch src/quad_rules.F90 && fobis build --quiet
[auto-discover] src not set; scanning: ./src

Building src/integrate.F90
Compiling src/quad_rules.F90 serially
Compiling src/integrands.F90 serially
Compiling src/integrate.F90 serially
Linking ./integrate
Target src/integrate.F90 has been successfully built

a rebuild after touching one file recompiles that file only

Recorded on a small demo project, not quad.

Cleaning ​

console
$ fobis clean && find . -type f | sort
Removing all *.(s)mod files into "mod"
Removing all *.o files into "obj"
./integrate
./obj/.fobis-compiler.json
./src/integrands.F90
./src/integrate.F90
./src/quad_rules.F90

fobis clean removes objects and module files. The executable stays: with no target named, FoBiS does not know which file it built (the next chapter names one).

What you learned

fobis build with no configuration: source discovery, dependency order from use statements, incremental rebuilds, fobis clean. Reference: build, clean.

Next: 2. A fobos file.