28
28
module fpm_backend
29
29
30
30
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
32
32
use fpm_filesystem, only: basename, dirname, join_path, exists, mkdir, run, getline
33
33
use fpm_model, only: fpm_model_t
34
34
use fpm_strings, only: string_t, operator (.in .)
35
35
use fpm_targets, only: build_target_t, build_target_ptr, FPM_TARGET_OBJECT, &
36
36
FPM_TARGET_C_OBJECT, FPM_TARGET_ARCHIVE, FPM_TARGET_EXECUTABLE, &
37
37
FPM_TARGET_CPP_OBJECT
38
38
use fpm_backend_output
39
+ use fpm_compile_commands, only: compile_command_table_t
39
40
implicit none
40
41
41
42
private
@@ -53,17 +54,22 @@ function c_isatty() bind(C, name = 'c_isatty')
53
54
contains
54
55
55
56
! > 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 )
57
58
type (build_target_ptr), intent (inout ) :: targets(:)
58
59
type (fpm_model_t), intent (in ) :: model
59
60
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
+
61
66
integer :: i, j
62
67
type (build_target_ptr), allocatable :: queue(:)
63
68
integer , allocatable :: schedule_ptr(:), stat(:)
64
69
logical :: build_failed, skip_current
65
70
type (string_t), allocatable :: build_dirs(:)
66
71
type (string_t) :: temp
72
+ type (error_t), allocatable :: error
67
73
68
74
type (build_progress_t) :: progress
69
75
logical :: plain_output
@@ -79,28 +85,27 @@ subroutine build_package(targets,model,verbose)
79
85
end do
80
86
81
87
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)
83
89
end do
84
90
85
91
! Perform depth-first topological sort of targets
86
92
do i= 1 ,size (targets)
87
93
88
- call sort_target(targets(i)% ptr)
94
+ call sort_target(targets(i)% ptr, dry_run )
89
95
90
96
end do
91
97
92
98
! Construct build schedule queue
93
99
call schedule_targets(queue, schedule_ptr, targets)
94
100
95
101
! 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
97
103
write (stderr, ' (a)' ) ' Project is up to date'
98
104
return
99
105
end if
100
106
101
107
! Initialise build status flags
102
- allocate (stat(size (queue)))
103
- stat(:) = 0
108
+ allocate (stat(size (queue)),source= 0 )
104
109
build_failed = .false.
105
110
106
111
! Set output mode
@@ -124,9 +129,10 @@ subroutine build_package(targets,model,verbose)
124
129
skip_current = build_failed
125
130
126
131
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))
130
136
end if
131
137
132
138
! Set global flag if this target failed to build
@@ -155,7 +161,9 @@ subroutine build_package(targets,model,verbose)
155
161
156
162
end do
157
163
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))
159
167
160
168
end subroutine build_package
161
169
@@ -172,15 +180,19 @@ end subroutine build_package
172
180
! > If `target` is marked as sorted, `target%schedule` should be an
173
181
! > integer greater than zero indicating the region for scheduling
174
182
! >
175
- recursive subroutine sort_target (target )
183
+ recursive subroutine sort_target (target , mock )
176
184
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
177
187
178
188
integer :: i, fh, stat
189
+ logical :: dry_run
190
+
191
+ dry_run = .false.
192
+ if (present (mock)) dry_run = mock
179
193
180
194
! 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
184
196
185
197
! Check for a circular dependency
186
198
! (If target has been touched but not processed)
@@ -193,20 +205,24 @@ recursive subroutine sort_target(target)
193
205
! Load cached source file digest if present
194
206
if (.not. allocated (target % digest_cached) .and. &
195
207
exists(target % output_file) .and. &
196
- exists(target % output_file// ' .digest' )) then
208
+ exists(target % output_file// ' .digest' ) .and. &
209
+ (.not. dry_run)) then
197
210
198
211
allocate (target % digest_cached)
199
212
open (newunit= fh,file= target % output_file// ' .digest' ,status= ' old' )
200
213
read (fh,* ,iostat= stat) target % digest_cached
201
214
close (fh)
202
215
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)
206
218
207
219
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
210
226
211
227
! Skip if target is source-based and source file is unmodified
212
228
if (allocated (target % digest_cached)) then
@@ -225,7 +241,7 @@ recursive subroutine sort_target(target)
225
241
do i= 1 ,size (target % dependencies)
226
242
227
243
! Sort dependency
228
- call sort_target(target % dependencies(i)% ptr)
244
+ call sort_target(target % dependencies(i)% ptr, dry_run )
229
245
230
246
if (.not. target % dependencies(i)% ptr% skip) then
231
247
@@ -300,16 +316,19 @@ end subroutine schedule_targets
300
316
! >
301
317
! > If successful, also caches the source file digest to disk.
302
318
! >
303
- subroutine build_target (model ,target ,verbose ,stat )
319
+ subroutine build_target (model ,target ,verbose ,dry_run , table , stat )
304
320
type (fpm_model_t), intent (in ) :: model
305
321
type (build_target_t), intent (in ), target :: target
306
322
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
307
326
integer , intent (out ) :: stat
308
327
309
328
integer :: fh
310
329
311
330
! $omp critical
312
- if (.not. exists(dirname(target % output_file))) then
331
+ if (.not. exists(dirname(target % output_file)) .and. .not. dry_run ) then
313
332
call mkdir(dirname(target % output_file),verbose)
314
333
end if
315
334
! $omp end critical
@@ -318,27 +337,27 @@ subroutine build_target(model,target,verbose,stat)
318
337
319
338
case (FPM_TARGET_OBJECT)
320
339
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 )
322
341
323
342
case (FPM_TARGET_C_OBJECT)
324
343
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 )
326
345
327
346
case (FPM_TARGET_CPP_OBJECT)
328
347
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 )
330
349
331
350
case (FPM_TARGET_EXECUTABLE)
332
351
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 )
334
353
335
354
case (FPM_TARGET_ARCHIVE)
336
355
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 )
338
357
339
358
end select
340
359
341
- if (stat == 0 .and. allocated (target % source)) then
360
+ if (stat == 0 .and. allocated (target % source) .and. .not. dry_run ) then
342
361
open (newunit= fh,file= target % output_file// ' .digest' ,status= ' unknown' )
343
362
write (fh,* ) target % source% digest
344
363
close (fh)
0 commit comments