run_banner.f90 Source File


Source Code

!> @file run_banner.f90
!> @brief One-line startup banner: version, compiler, build profile, MPI ranks.
!! Version string is sourced from `version_info:solver_build()`.
module run_banner
  use, intrinsic :: iso_fortran_env, only: compiler_version, compiler_options
  use logger, only: log_info
  use mpi_runtime, only: n_ranks
  use version_info, only: solver_build
  implicit none
  private

  public :: classify_build
  public :: format_run_banner
  public :: log_run_banner

contains

  !> Classify a compiler_options() string as debug/release/unknown and return
  !! the optimization flag actually present. Pure string scan; the last '-O'
  !! token wins (compilers honour the last -O). No MPI / no intrinsics.
  pure subroutine classify_build(options, label, opt_flag)
    character(len=*), intent(in) :: options
    character(len=*), intent(out) :: label
    character(len=*), intent(out) :: opt_flag
    integer :: i, p, q

    opt_flag = '-O?'

    ! Last '-O' that starts a token (string start, or preceded by a space).
    p = 0
    do i = 1, len_trim(options) - 1
      if (options(i:i + 1) == '-O') then
        if (i == 1) then
          p = i
        else if (options(i - 1:i - 1) == ' ') then
          p = i
        end if
      end if
    end do

    if (p > 0) then
      q = p
      do while (q <= len_trim(options))
        if (options(q:q) == ' ') exit
        q = q + 1
      end do
      opt_flag = options(p:q - 1)
    end if

    select case (trim(opt_flag))
    case ('-O0')
      label = 'debug'
    case ('-O2', '-O3', '-Ofast')
      label = 'release'
    case default
      ! No release/debug -O level matched. fpm's debug profile passes no -O
      ! at all (just -g / -fcheck / -fbacktrace), so fall back to a
      ! debug-symbol signal before giving up with 'unknown'.
      if (index(' '//trim(options)//' ', ' -g ') > 0 .or. &
          index(options, '-fcheck') > 0 .or. &
          index(options, '-fbacktrace') > 0) then
        label = 'debug'
        if (trim(opt_flag) == '-O?') opt_flag = '-g'
      else
        label = 'unknown'
      end if
    end select
  end subroutine classify_build

  !> Assemble the single ASCII banner line. Pure; ASCII separators only so it
  !! renders correctly on Windows consoles as well.
  !! `version` must include the leading `v` (e.g. `v0.1.0-dev`).
  !! Optional `prog` overrides the default program name `euler_1d`.
  pure function format_run_banner(version, compiler, label, opt_flag, n_rank, prog) &
    result(line)
    character(len=*), intent(in) :: version, compiler, label, opt_flag
    integer, intent(in) :: n_rank
    character(len=*), intent(in), optional :: prog
    character(len=:), allocatable :: line
    character(len=16) :: ranks_str
    character(len=:), allocatable :: name

    name = 'euler_1d'
    if (present(prog)) name = trim(prog)
    write (ranks_str, '(I0)') n_rank
    line = name//' '//trim(version)//' | '//trim(compiler)// &
           ' | '//trim(label)//' ('//trim(opt_flag)//')'// &
           ' | MPI ranks: '//trim(ranks_str)
  end function format_run_banner

  !> Emit the one-line startup banner via log_info on rank 0.
  !! log_info's private emit already returns early on ranks > 0 (unless
  !! LOG_ALL_RANKS), so no explicit rank guard is needed here.
  !! Optional `name` overrides the default program name `euler_1d`.
  subroutine log_run_banner(name)
    character(len=*), intent(in), optional :: name
    character(len=:), allocatable :: options, compiler
    character(len=16) :: label, opt_flag

    options = compiler_options()
    compiler = compiler_version()
    call classify_build(options, label, opt_flag)
    if (present(name)) then
      call log_info(format_run_banner(solver_build(), compiler, &
                                      label, opt_flag, n_ranks(), name))
    else
      call log_info(format_run_banner(solver_build(), compiler, &
                                      label, opt_flag, n_ranks()))
    end if
  end subroutine log_run_banner

end module run_banner