Skip to content

Commit 8293f55

Browse files
authored
feat: export compile_commands.json (#1129)
2 parents 0ce98c0 + b80ce7c commit 8293f55

12 files changed

+1040
-242
lines changed

fpm.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ fortran-regex.tag = "1.1.2"
2121
jonquil.git = "https://github.com/toml-f/jonquil"
2222
jonquil.rev = "4fbd4cf34d577c0fd25e32667ee9e41bf231ece8"
2323
fortran-shlex.git = "https://github.com/perazz/fortran-shlex"
24-
fortran-shlex.tag = "1.2.1"
24+
fortran-shlex.tag = "2.0.0"
2525

2626
[[test]]
2727
name = "cli-test"

src/fpm.f90

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -461,10 +461,11 @@ subroutine cmd_build(settings)
461461
do i=1,size(targets)
462462
write(stderr,*) targets(i)%ptr%output_file
463463
enddo
464-
else if (settings%show_model) then
464+
endif
465+
if (settings%show_model) then
465466
call show_model(model)
466467
else
467-
call build_package(targets,model,verbose=settings%verbose)
468+
call build_package(targets,model,verbose=settings%verbose,dry_run=settings%list)
468469
endif
469470

470471
end subroutine cmd_build
@@ -573,7 +574,7 @@ subroutine cmd_run(settings,test)
573574

574575
end if
575576

576-
call build_package(targets,model,verbose=settings%verbose)
577+
call build_package(targets,model,verbose=settings%verbose,dry_run=settings%list)
577578

578579
if (settings%list) then
579580
call compact_list()

src/fpm/cmd/install.f90

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

5555
if (.not.settings%no_rebuild) then
56-
call build_package(targets,model,verbose=settings%verbose)
56+
call build_package(targets,model,verbose=settings%verbose,dry_run=settings%list)
5757
end if
5858

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

src/fpm/toml.f90

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ module fpm_toml
7878
module procedure get_logical
7979
module procedure get_integer
8080
module procedure get_integer_64
81+
module procedure get_char
82+
module procedure get_string
8183
end interface get_value
8284

8385

@@ -704,6 +706,57 @@ subroutine get_integer(table, key, var, error, whereAt)
704706

705707
end subroutine get_integer
706708

709+
!> Function wrapper to get a default string variable from a toml table, returning an fpm error
710+
subroutine get_string(table, key, var, error, whereAt)
711+
712+
!> Instance of the TOML data structure
713+
type(toml_table), intent(inout) :: table
714+
715+
!> The key
716+
character(len=*), intent(in) :: key
717+
718+
!> The variable
719+
type(string_t), intent(inout) :: var
720+
721+
!> Error handling
722+
type(error_t), allocatable, intent(out) :: error
723+
724+
!> Optional description
725+
character(len=*), intent(in), optional :: whereAt
726+
727+
call get_char(table, key, var%s, error, whereAt)
728+
729+
end subroutine get_string
730+
731+
!> Function wrapper to get a default character variable from a toml table, returning an fpm error
732+
subroutine get_char(table, key, var, error, whereAt)
733+
734+
!> Instance of the TOML data structure
735+
type(toml_table), intent(inout) :: table
736+
737+
!> The key
738+
character(len=*), intent(in) :: key
739+
740+
!> The variable
741+
character(len=:), allocatable, intent(inout) :: var
742+
743+
!> Error handling
744+
type(error_t), allocatable, intent(out) :: error
745+
746+
!> Optional description
747+
character(len=*), intent(in), optional :: whereAt
748+
749+
integer :: ierr
750+
751+
call get_value(table, key, var, stat=ierr)
752+
if (ierr/=toml_stat%success) then
753+
call fatal_error(error,'cannot get string key <'//key//'> from TOML table')
754+
if (present(whereAt)) error%message = whereAt//': '//error%message
755+
return
756+
end if
757+
758+
end subroutine get_char
759+
707760
!> Function wrapper to get a integer(int64) variable from a toml table, returning an fpm error
708761
subroutine get_integer_64(table, key, var, error, whereAt)
709762

src/fpm_backend.F90

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,15 @@
2828
module fpm_backend
2929

3030
use,intrinsic :: iso_fortran_env, only : stdin=>input_unit, stdout=>output_unit, stderr=>error_unit
31-
use fpm_error, only : fpm_stop
31+
use fpm_error, only : fpm_stop, error_t
3232
use fpm_filesystem, only: basename, dirname, join_path, exists, mkdir, run, getline
3333
use fpm_model, only: fpm_model_t
3434
use fpm_strings, only: string_t, operator(.in.)
3535
use fpm_targets, only: build_target_t, build_target_ptr, FPM_TARGET_OBJECT, &
3636
FPM_TARGET_C_OBJECT, FPM_TARGET_ARCHIVE, FPM_TARGET_EXECUTABLE, &
3737
FPM_TARGET_CPP_OBJECT
3838
use fpm_backend_output
39+
use fpm_compile_commands, only: compile_command_table_t
3940
implicit none
4041

4142
private
@@ -53,17 +54,22 @@ function c_isatty() bind(C, name = 'c_isatty')
5354
contains
5455

5556
!> Top-level routine to build package described by `model`
56-
subroutine build_package(targets,model,verbose)
57+
subroutine build_package(targets,model,verbose,dry_run)
5758
type(build_target_ptr), intent(inout) :: targets(:)
5859
type(fpm_model_t), intent(in) :: model
5960
logical, intent(in) :: verbose
60-
61+
62+
!> If dry_run, the build process is only mocked, but the list of compile_commands
63+
!> is still created
64+
logical, intent(in) :: dry_run
65+
6166
integer :: i, j
6267
type(build_target_ptr), allocatable :: queue(:)
6368
integer, allocatable :: schedule_ptr(:), stat(:)
6469
logical :: build_failed, skip_current
6570
type(string_t), allocatable :: build_dirs(:)
6671
type(string_t) :: temp
72+
type(error_t), allocatable :: error
6773

6874
type(build_progress_t) :: progress
6975
logical :: plain_output
@@ -79,28 +85,27 @@ subroutine build_package(targets,model,verbose)
7985
end do
8086

8187
do i = 1, size(build_dirs)
82-
call mkdir(build_dirs(i)%s,verbose)
88+
if (.not.dry_run) call mkdir(build_dirs(i)%s,verbose)
8389
end do
8490

8591
! Perform depth-first topological sort of targets
8692
do i=1,size(targets)
8793

88-
call sort_target(targets(i)%ptr)
94+
call sort_target(targets(i)%ptr, dry_run)
8995

9096
end do
9197

9298
! Construct build schedule queue
9399
call schedule_targets(queue, schedule_ptr, targets)
94100

95101
! Check if queue is empty
96-
if (.not.verbose .and. size(queue) < 1) then
102+
if (.not.verbose .and. size(queue) < 1 .and. .not.dry_run) then
97103
write(stderr, '(a)') 'Project is up to date'
98104
return
99105
end if
100106

101107
! Initialise build status flags
102-
allocate(stat(size(queue)))
103-
stat(:) = 0
108+
allocate(stat(size(queue)),source=0)
104109
build_failed = .false.
105110

106111
! Set output mode
@@ -124,9 +129,10 @@ subroutine build_package(targets,model,verbose)
124129
skip_current = build_failed
125130

126131
if (.not.skip_current) then
127-
call progress%compiling_status(j)
128-
call build_target(model,queue(j)%ptr,verbose,stat(j))
129-
call progress%completed_status(j,stat(j))
132+
if (.not.dry_run) call progress%compiling_status(j)
133+
call build_target(model,queue(j)%ptr,verbose,dry_run, &
134+
progress%compile_commands,stat(j))
135+
if (.not.dry_run) call progress%completed_status(j,stat(j))
130136
end if
131137

132138
! Set global flag if this target failed to build
@@ -155,7 +161,9 @@ subroutine build_package(targets,model,verbose)
155161

156162
end do
157163

158-
call progress%success()
164+
if (.not.dry_run) call progress%success()
165+
call progress%dump_commands(error)
166+
if (allocated(error)) call fpm_stop(1,'error writing compile_commands.json: '//trim(error%message))
159167

160168
end subroutine build_package
161169

@@ -172,15 +180,19 @@ end subroutine build_package
172180
!> If `target` is marked as sorted, `target%schedule` should be an
173181
!> integer greater than zero indicating the region for scheduling
174182
!>
175-
recursive subroutine sort_target(target)
183+
recursive subroutine sort_target(target, mock)
176184
type(build_target_t), intent(inout), target :: target
185+
!> Optionally sort ALL targets if this is a dry run
186+
logical, optional, intent(in) :: mock
177187

178188
integer :: i, fh, stat
189+
logical :: dry_run
190+
191+
dry_run = .false.
192+
if (present(mock)) dry_run = mock
179193

180194
! Check if target has already been processed (as a dependency)
181-
if (target%sorted .or. target%skip) then
182-
return
183-
end if
195+
if (target%sorted .or. target%skip) return
184196

185197
! Check for a circular dependency
186198
! (If target has been touched but not processed)
@@ -193,20 +205,24 @@ recursive subroutine sort_target(target)
193205
! Load cached source file digest if present
194206
if (.not.allocated(target%digest_cached) .and. &
195207
exists(target%output_file) .and. &
196-
exists(target%output_file//'.digest')) then
208+
exists(target%output_file//'.digest') .and. &
209+
(.not.dry_run)) then
197210

198211
allocate(target%digest_cached)
199212
open(newunit=fh,file=target%output_file//'.digest',status='old')
200213
read(fh,*,iostat=stat) target%digest_cached
201214
close(fh)
202215

203-
if (stat /= 0) then ! Cached digest is not recognized
204-
deallocate(target%digest_cached)
205-
end if
216+
! Cached digest is not recognized
217+
if (stat /= 0) deallocate(target%digest_cached)
206218

207219
end if
208-
209-
if (allocated(target%source)) then
220+
221+
if (dry_run) then
222+
223+
target%skip = .false.
224+
225+
elseif (allocated(target%source)) then
210226

211227
! Skip if target is source-based and source file is unmodified
212228
if (allocated(target%digest_cached)) then
@@ -225,7 +241,7 @@ recursive subroutine sort_target(target)
225241
do i=1,size(target%dependencies)
226242

227243
! Sort dependency
228-
call sort_target(target%dependencies(i)%ptr)
244+
call sort_target(target%dependencies(i)%ptr, dry_run)
229245

230246
if (.not.target%dependencies(i)%ptr%skip) then
231247

@@ -300,16 +316,19 @@ end subroutine schedule_targets
300316
!>
301317
!> If successful, also caches the source file digest to disk.
302318
!>
303-
subroutine build_target(model,target,verbose,stat)
319+
subroutine build_target(model,target,verbose,dry_run,table,stat)
304320
type(fpm_model_t), intent(in) :: model
305321
type(build_target_t), intent(in), target :: target
306322
logical, intent(in) :: verbose
323+
!> If dry_run, the build process is only mocked, but compile_commands are still created
324+
logical, intent(in) :: dry_run
325+
type(compile_command_table_t), intent(inout) :: table
307326
integer, intent(out) :: stat
308327

309328
integer :: fh
310329

311330
!$omp critical
312-
if (.not.exists(dirname(target%output_file))) then
331+
if (.not.exists(dirname(target%output_file)) .and. .not.dry_run) then
313332
call mkdir(dirname(target%output_file),verbose)
314333
end if
315334
!$omp end critical
@@ -318,27 +337,27 @@ subroutine build_target(model,target,verbose,stat)
318337

319338
case (FPM_TARGET_OBJECT)
320339
call model%compiler%compile_fortran(target%source%file_name, target%output_file, &
321-
& target%compile_flags, target%output_log_file, stat)
340+
& target%compile_flags, target%output_log_file, stat, table, dry_run)
322341

323342
case (FPM_TARGET_C_OBJECT)
324343
call model%compiler%compile_c(target%source%file_name, target%output_file, &
325-
& target%compile_flags, target%output_log_file, stat)
344+
& target%compile_flags, target%output_log_file, stat, table, dry_run)
326345

327346
case (FPM_TARGET_CPP_OBJECT)
328347
call model%compiler%compile_cpp(target%source%file_name, target%output_file, &
329-
& target%compile_flags, target%output_log_file, stat)
348+
& target%compile_flags, target%output_log_file, stat, table, dry_run)
330349

331350
case (FPM_TARGET_EXECUTABLE)
332351
call model%compiler%link(target%output_file, &
333-
& target%compile_flags//" "//target%link_flags, target%output_log_file, stat)
352+
& target%compile_flags//" "//target%link_flags, target%output_log_file, stat, dry_run)
334353

335354
case (FPM_TARGET_ARCHIVE)
336355
call model%archiver%make_archive(target%output_file, target%link_objects, &
337-
& target%output_log_file, stat)
356+
& target%output_log_file, stat, dry_run)
338357

339358
end select
340359

341-
if (stat == 0 .and. allocated(target%source)) then
360+
if (stat == 0 .and. allocated(target%source) .and. .not.dry_run) then
342361
open(newunit=fh,file=target%output_file//'.digest',status='unknown')
343362
write(fh,*) target%source%digest
344363
close(fh)

0 commit comments

Comments
 (0)