Skip to content

Commit 2c6148e

Browse files
authored
Merge pull request #622 from LKedward/backend-output
Cleanup the backend output
2 parents 57b5636 + d2009f1 commit 2c6148e

17 files changed

+813
-77
lines changed

.github/workflows/CI.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,20 @@ jobs:
5959
--slave /usr/bin/gfortran gfortran /usr/bin/gfortran-${GCC_V} \
6060
--slave /usr/bin/gcov gcov /usr/bin/gcov-${GCC_V}
6161
62+
- name: Install GFortran Windows
63+
if: contains(matrix.os, 'windows')
64+
run: |
65+
Invoke-WebRequest -Uri $Env:GCC_DOWNLOAD -OutFile mingw-w64.zip
66+
Expand-Archive mingw-w64.zip
67+
echo "$pwd\mingw-w64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
68+
env:
69+
GCC_DOWNLOAD: "https://github.com/brechtsanders/winlibs_mingw/releases/download/9.4.0-9.0.0-msvcrt-r2/winlibs-x86_64-posix-seh-gcc-9.4.0-mingw-w64-9.0.0-r2.zip"
70+
6271
# Phase 1: Bootstrap fpm with existing version
6372
- name: Install fpm
6473
uses: fortran-lang/setup-fpm@v3
6574
with:
66-
fpm-version: 'v0.2.0'
75+
fpm-version: 'v0.3.0'
6776

6877
- name: Remove fpm from path
6978
shell: bash

src/fpm.f90

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ module fpm
44
use fpm_command_line, only: fpm_build_settings, fpm_new_settings, &
55
fpm_run_settings, fpm_install_settings, fpm_test_settings
66
use fpm_dependency, only : new_dependency_tree
7-
use fpm_environment, only: run, get_env
8-
use fpm_filesystem, only: is_dir, join_path, number_of_rows, list_files, exists, basename, filewrite, mkdir
7+
use fpm_environment, only: get_env
8+
use fpm_filesystem, only: is_dir, join_path, number_of_rows, list_files, exists, &
9+
basename, filewrite, mkdir, run
910
use fpm_model, only: fpm_model_t, srcfile_t, show_model, &
1011
FPM_SCOPE_UNKNOWN, FPM_SCOPE_LIB, FPM_SCOPE_DEP, &
1112
FPM_SCOPE_APP, FPM_SCOPE_EXAMPLE, FPM_SCOPE_TEST
@@ -59,8 +60,10 @@ subroutine build_model(model, settings, package, error)
5960
call filewrite(join_path("build", ".gitignore"),["*"])
6061
end if
6162

62-
call new_compiler(model%compiler, settings%compiler, settings%c_compiler)
63-
call new_archiver(model%archiver, settings%archiver)
63+
call new_compiler(model%compiler, settings%compiler, settings%c_compiler, &
64+
& echo=settings%verbose, verbose=settings%verbose)
65+
call new_archiver(model%archiver, settings%archiver, &
66+
& echo=settings%verbose, verbose=settings%verbose)
6467

6568
if (settings%flag == '') then
6669
flags = model%compiler%get_default_flags(settings%profile == "release")
@@ -284,7 +287,7 @@ subroutine cmd_build(settings)
284287
else if (settings%show_model) then
285288
call show_model(model)
286289
else
287-
call build_package(targets,model)
290+
call build_package(targets,model,verbose=settings%verbose)
288291
endif
289292

290293
end subroutine cmd_build
@@ -415,7 +418,7 @@ subroutine cmd_run(settings,test)
415418

416419
end if
417420

418-
call build_package(targets,model)
421+
call build_package(targets,model,verbose=settings%verbose)
419422

420423
if (settings%list) then
421424
call compact_list()

src/fpm/cmd/install.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ subroutine cmd_install(settings)
5454
end if
5555

5656
if (.not.settings%no_rebuild) then
57-
call build_package(targets,model)
57+
call build_package(targets,model,verbose=settings%verbose)
5858
end if
5959

6060
call new_installer(installer, prefix=settings%prefix, &

src/fpm/cmd/new.f90

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ module fpm_cmd_new
5454
!> be the first go-to for a CLI utility).
5555

5656
use fpm_command_line, only : fpm_new_settings
57-
use fpm_environment, only : run, OS_LINUX, OS_MACOS, OS_WINDOWS
57+
use fpm_environment, only : OS_LINUX, OS_MACOS, OS_WINDOWS
5858
use fpm_filesystem, only : join_path, exists, basename, mkdir, is_dir
59-
use fpm_filesystem, only : fileopen, fileclose, filewrite, warnwrite, which
59+
use fpm_filesystem, only : fileopen, fileclose, filewrite, warnwrite, which, run
6060
use fpm_strings, only : join, to_fortran_name
6161
use fpm_error, only : fpm_stop
6262

src/fpm_backend.f90 renamed to src/fpm_backend.F90

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,33 @@ module fpm_backend
2929

3030
use,intrinsic :: iso_fortran_env, only : stdin=>input_unit, stdout=>output_unit, stderr=>error_unit
3131
use fpm_error, only : fpm_stop
32-
use fpm_environment, only: run, get_os_type, OS_WINDOWS
33-
use fpm_filesystem, only: basename, dirname, join_path, exists, mkdir
32+
use fpm_filesystem, only: basename, dirname, join_path, exists, mkdir, run, getline
3433
use fpm_model, only: fpm_model_t
3534
use fpm_strings, only: string_t, operator(.in.)
3635
use fpm_targets, only: build_target_t, build_target_ptr, FPM_TARGET_OBJECT, &
3736
FPM_TARGET_C_OBJECT, FPM_TARGET_ARCHIVE, FPM_TARGET_EXECUTABLE
37+
use fpm_backend_output
3838
implicit none
3939

4040
private
4141
public :: build_package, sort_target, schedule_targets
4242

43+
#ifndef FPM_BOOTSTRAP
44+
interface
45+
function c_isatty() bind(C, name = 'c_isatty')
46+
use, intrinsic :: iso_c_binding, only: c_int
47+
integer(c_int) :: c_isatty
48+
end function
49+
end interface
50+
#endif
51+
4352
contains
4453

4554
!> Top-level routine to build package described by `model`
46-
subroutine build_package(targets,model)
55+
subroutine build_package(targets,model,verbose)
4756
type(build_target_ptr), intent(inout) :: targets(:)
4857
type(fpm_model_t), intent(in) :: model
58+
logical, intent(in) :: verbose
4959

5060
integer :: i, j
5161
type(build_target_ptr), allocatable :: queue(:)
@@ -54,6 +64,9 @@ subroutine build_package(targets,model)
5464
type(string_t), allocatable :: build_dirs(:)
5565
type(string_t) :: temp
5666

67+
type(build_progress_t) :: progress
68+
logical :: plain_output
69+
5770
! Need to make output directory for include (mod) files
5871
allocate(build_dirs(0))
5972
do i = 1, size(targets)
@@ -65,7 +78,7 @@ subroutine build_package(targets,model)
6578
end do
6679

6780
do i = 1, size(build_dirs)
68-
call mkdir(build_dirs(i)%s)
81+
call mkdir(build_dirs(i)%s,verbose)
6982
end do
7083

7184
! Perform depth-first topological sort of targets
@@ -78,11 +91,26 @@ subroutine build_package(targets,model)
7891
! Construct build schedule queue
7992
call schedule_targets(queue, schedule_ptr, targets)
8093

94+
! Check if queue is empty
95+
if (.not.verbose .and. size(queue) < 1) then
96+
write(*, '(a)') 'Project is up to date'
97+
return
98+
end if
99+
81100
! Initialise build status flags
82101
allocate(stat(size(queue)))
83102
stat(:) = 0
84103
build_failed = .false.
85104

105+
! Set output mode
106+
#ifndef FPM_BOOTSTRAP
107+
plain_output = (.not.(c_isatty()==1)) .or. verbose
108+
#else
109+
plain_output = .true.
110+
#endif
111+
112+
progress = build_progress_t(queue,plain_output)
113+
86114
! Loop over parallel schedule regions
87115
do i=1,size(schedule_ptr)-1
88116

@@ -95,7 +123,9 @@ subroutine build_package(targets,model)
95123
skip_current = build_failed
96124

97125
if (.not.skip_current) then
98-
call build_target(model,queue(j)%ptr,stat(j))
126+
call progress%compiling_status(j)
127+
call build_target(model,queue(j)%ptr,verbose,stat(j))
128+
call progress%completed_status(j,stat(j))
99129
end if
100130

101131
! Set global flag if this target failed to build
@@ -108,6 +138,12 @@ subroutine build_package(targets,model)
108138

109139
! Check if this schedule region failed: exit with message if failed
110140
if (build_failed) then
141+
write(*,*)
142+
do j=1,size(stat)
143+
if (stat(j) /= 0) Then
144+
call print_build_log(queue(j)%ptr)
145+
end if
146+
end do
111147
do j=1,size(stat)
112148
if (stat(j) /= 0) then
113149
write(stderr,'(*(g0:,1x))') '<ERROR> Compilation failed for object "',basename(queue(j)%ptr%output_file),'"'
@@ -118,6 +154,8 @@ subroutine build_package(targets,model)
118154

119155
end do
120156

157+
call progress%success()
158+
121159
end subroutine build_package
122160

123161

@@ -261,35 +299,37 @@ end subroutine schedule_targets
261299
!>
262300
!> If successful, also caches the source file digest to disk.
263301
!>
264-
subroutine build_target(model,target,stat)
302+
subroutine build_target(model,target,verbose,stat)
265303
type(fpm_model_t), intent(in) :: model
266304
type(build_target_t), intent(in), target :: target
305+
logical, intent(in) :: verbose
267306
integer, intent(out) :: stat
268307

269308
integer :: fh
270309

271310
!$omp critical
272311
if (.not.exists(dirname(target%output_file))) then
273-
call mkdir(dirname(target%output_file))
312+
call mkdir(dirname(target%output_file),verbose)
274313
end if
275314
!$omp end critical
276315

277316
select case(target%target_type)
278317

279318
case (FPM_TARGET_OBJECT)
280319
call model%compiler%compile_fortran(target%source%file_name, target%output_file, &
281-
& target%compile_flags, stat)
320+
& target%compile_flags, target%output_log_file, stat)
282321

283322
case (FPM_TARGET_C_OBJECT)
284323
call model%compiler%compile_c(target%source%file_name, target%output_file, &
285-
& target%compile_flags, stat)
324+
& target%compile_flags, target%output_log_file, stat)
286325

287326
case (FPM_TARGET_EXECUTABLE)
288327
call model%compiler%link(target%output_file, &
289-
& target%compile_flags//" "//target%link_flags, stat)
328+
& target%compile_flags//" "//target%link_flags, target%output_log_file, stat)
290329

291330
case (FPM_TARGET_ARCHIVE)
292-
call model%archiver%make_archive(target%output_file, target%link_objects, stat)
331+
call model%archiver%make_archive(target%output_file, target%link_objects, &
332+
& target%output_log_file, stat)
293333

294334
end select
295335

@@ -302,4 +342,30 @@ subroutine build_target(model,target,stat)
302342
end subroutine build_target
303343

304344

345+
!> Read and print the build log for target
346+
!>
347+
subroutine print_build_log(target)
348+
type(build_target_t), intent(in), target :: target
349+
350+
integer :: fh, ios
351+
character(:), allocatable :: line
352+
353+
if (exists(target%output_log_file)) then
354+
355+
open(newunit=fh,file=target%output_log_file,status='old')
356+
do
357+
call getline(fh, line, ios)
358+
if (ios /= 0) exit
359+
write(*,'(A)') trim(line)
360+
end do
361+
close(fh)
362+
363+
else
364+
365+
write(stderr,'(*(g0:,1x))') '<ERROR> Unable to find build log "',basename(target%output_log_file),'"'
366+
367+
end if
368+
369+
end subroutine print_build_log
370+
305371
end module fpm_backend

src/fpm_backend_console.f90

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
!># Build Backend Console
2+
!> This module provides a lightweight implementation for printing to the console
3+
!> and updating previously-printed console lines. It used by `[[fpm_backend_output]]`
4+
!> for pretty-printing build status and progress.
5+
!>
6+
!> @note The implementation for updating previous lines relies on no other output
7+
!> going to `stdout`/`stderr` except through the `console_t` object provided.
8+
!>
9+
!> @note All write statements to `stdout` are enclosed within OpenMP `critical` regions
10+
!>
11+
module fpm_backend_console
12+
use iso_fortran_env, only: stdout=>output_unit
13+
implicit none
14+
15+
private
16+
public :: console_t
17+
public :: LINE_RESET
18+
public :: COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_RESET
19+
20+
character(len=*), parameter :: ESC = char(27)
21+
!> Escape code for erasing current line
22+
character(len=*), parameter :: LINE_RESET = ESC//"[2K"//ESC//"[1G"
23+
!> Escape code for moving up one line
24+
character(len=*), parameter :: LINE_UP = ESC//"[1A"
25+
!> Escape code for moving down one line
26+
character(len=*), parameter :: LINE_DOWN = ESC//"[1B"
27+
!> Escape code for red foreground color
28+
character(len=*), parameter :: COLOR_RED = ESC//"[31m"
29+
!> Escape code for green foreground color
30+
character(len=*), parameter :: COLOR_GREEN = ESC//"[32m"
31+
!> Escape code for yellow foreground color
32+
character(len=*), parameter :: COLOR_YELLOW = ESC//"[93m"
33+
!> Escape code to reset foreground color
34+
character(len=*), parameter :: COLOR_RESET = ESC//"[0m"
35+
36+
!> Console object
37+
type console_t
38+
!> Number of lines printed
39+
integer :: n_line = 1
40+
41+
contains
42+
!> Write a single line to the console
43+
procedure :: write_line => console_write_line
44+
!> Update a previously-written console line
45+
procedure :: update_line => console_update_line
46+
end type console_t
47+
48+
contains
49+
50+
!> Write a single line to the standard output
51+
subroutine console_write_line(console,str,line,advance)
52+
!> Console object
53+
class(console_t), intent(inout) :: console
54+
!> String to write
55+
character(*), intent(in) :: str
56+
!> Integer needed to later update console line
57+
integer, intent(out), optional :: line
58+
!> Advancing output (print newline?)
59+
logical, intent(in), optional :: advance
60+
61+
character(3) :: adv
62+
63+
adv = "yes"
64+
if (present(advance)) then
65+
if (.not.advance) then
66+
adv = "no"
67+
end if
68+
end if
69+
70+
!$omp critical
71+
72+
if (present(line)) then
73+
line = console%n_line
74+
end if
75+
76+
write(stdout,'(A)',advance=trim(adv)) LINE_RESET//str
77+
78+
if (adv=="yes") then
79+
console%n_line = console%n_line + 1
80+
end if
81+
82+
!$omp end critical
83+
84+
end subroutine console_write_line
85+
86+
!> Overwrite a previously-written line in standard output
87+
subroutine console_update_line(console,line_no,str)
88+
!> Console object
89+
class(console_t), intent(in) :: console
90+
!> Integer output from `[[console_write_line]]`
91+
integer, intent(in) :: line_no
92+
!> New string to overwrite line
93+
character(*), intent(in) :: str
94+
95+
integer :: n
96+
97+
!$omp critical
98+
99+
n = console%n_line - line_no
100+
101+
! Step back to line
102+
write(stdout,'(A)',advance="no") repeat(LINE_UP,n)//LINE_RESET
103+
104+
write(stdout,'(A)') str
105+
106+
! Step forward to end
107+
write(stdout,'(A)',advance="no") repeat(LINE_DOWN,n)//LINE_RESET
108+
109+
!$omp end critical
110+
111+
end subroutine console_update_line
112+
113+
end module fpm_backend_console

0 commit comments

Comments
 (0)