!> @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