Skip to content

Commit 895f774

Browse files
committed
Update: to detect exported C API in modules via bind(C)
Sources containing module subroutines and functions with bind(C) are labelled as SUBPROGRAM to disable pruning.
1 parent 722a325 commit 895f774

File tree

2 files changed

+107
-2
lines changed

2 files changed

+107
-2
lines changed

src/fpm_source_parsing.f90

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ function parse_f_source(f_filename,error) result(f_source)
7676
type(srcfile_t) :: f_source
7777
type(error_t), allocatable, intent(out) :: error
7878

79-
logical :: inside_module
79+
logical :: inside_module, inside_interface
8080
integer :: stat
8181
integer :: fh, n_use, n_include, n_mod, n_parent, i, j, ic, pass
8282
type(string_t), allocatable :: file_lines(:), file_lines_lower(:)
@@ -111,6 +111,7 @@ function parse_f_source(f_filename,error) result(f_source)
111111
n_mod = 0
112112
n_parent = 0
113113
inside_module = .false.
114+
inside_interface = .false.
114115
file_loop: do i=1,size(file_lines_lower)
115116

116117
! Skip comment lines and preprocessor directives
@@ -120,6 +121,36 @@ function parse_f_source(f_filename,error) result(f_source)
120121
cycle
121122
end if
122123

124+
! Detect exported C-API via bind(C)
125+
if (.not.inside_interface .and. &
126+
index(file_lines_lower(i)%s,'bind(c') > 0) then
127+
128+
do j=i,1,-1
129+
130+
if (index(file_lines_lower(j)%s,'function') > 0 .or. &
131+
index(file_lines_lower(j)%s,'subroutine') > 0) then
132+
f_source%unit_type = FPM_UNIT_SUBPROGRAM
133+
exit
134+
end if
135+
136+
if (j>1) then
137+
138+
ic = index(file_lines_lower(j-1)%s,'!')
139+
if (ic < 1) then
140+
ic = len(file_lines_lower(j-1)%s)
141+
end if
142+
143+
temp_string = trim(file_lines_lower(j-1)%s(1:ic))
144+
if (index(temp_string,'&') /= len(temp_string)) then
145+
exit
146+
end if
147+
148+
end if
149+
150+
end do
151+
152+
end if
153+
123154
! Skip lines that are continued: not statements
124155
if (i > 1) then
125156
ic = index(file_lines_lower(i-1)%s,'!')
@@ -132,6 +163,27 @@ function parse_f_source(f_filename,error) result(f_source)
132163
end if
133164
end if
134165

166+
! Detect beginning of interface block
167+
if (index(file_lines_lower(i)%s,'interface') == 1) then
168+
169+
inside_interface = .true.
170+
cycle
171+
172+
end if
173+
174+
! Detect end of interface block
175+
if (index(file_lines_lower(i)%s,'end') == 1 .and. &
176+
len(file_lines_lower(i)%s) > 3) then
177+
178+
if (index(adjustl(file_lines_lower(i)%s(4:)),'interface') == 1) then
179+
180+
inside_interface = .false.
181+
cycle
182+
183+
end if
184+
185+
end if
186+
135187
! Process 'USE' statements
136188
if (index(file_lines_lower(i)%s,'use ') == 1 .or. &
137189
index(file_lines_lower(i)%s,'use::') == 1) then

test/fpm_test/test_source_parsing.f90

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ subroutine collect_source_parsing(testsuite)
2727
& new_unittest("program", test_program), &
2828
& new_unittest("module", test_module), &
2929
& new_unittest("module-with-subprogram", test_module_with_subprogram), &
30+
& new_unittest("module-with-c-api", test_module_with_c_api), &
3031
& new_unittest("module-end-stmt", test_module_end_stmt), &
3132
& new_unittest("program-with-module", test_program_with_module), &
3233
& new_unittest("submodule", test_submodule), &
@@ -315,7 +316,7 @@ subroutine test_module(error)
315316
& 'module my_mod ! A trailing comment', &
316317
& 'use module_one', &
317318
& 'interface', &
318-
& ' module subroutine f()', &
319+
& ' module subroutine f() bind(C)', &
319320
& 'end interface', &
320321
& 'integer :: program', &
321322
& 'program = 1', &
@@ -325,6 +326,10 @@ subroutine test_module(error)
325326
& 'contains', &
326327
& 'module subroutine&', &
327328
& ' e()', &
329+
& ' integer, parameter :: c = 1', &
330+
& ' integer :: & ', &
331+
& ' bind(c)', &
332+
& ' bind(c) = 1', &
328333
& 'end subroutine e', &
329334
& 'module subroutine f()', &
330335
& 'end subroutine f', &
@@ -489,6 +494,54 @@ subroutine test_module_end_stmt(error)
489494
end subroutine test_module_end_stmt
490495

491496

497+
!> Try to parse fortran module with exported C-API via bind(c)
498+
!> (this should be detected as FPM_UNIT_SUBPROGRAM not FPM_UNIT_MODULE to prevent pruning)
499+
subroutine test_module_with_c_api(error)
500+
501+
!> Error handling
502+
type(error_t), allocatable, intent(out) :: error
503+
504+
integer :: unit
505+
character(:), allocatable :: temp_file
506+
type(srcfile_t), allocatable :: f_source
507+
508+
allocate(temp_file, source=get_temp_filename())
509+
510+
open(file=temp_file, newunit=unit)
511+
write(unit, '(a)') &
512+
& 'module my_mod', &
513+
& 'contains', &
514+
& 'subroutine f() &', &
515+
& ' bind(C)', &
516+
& 'end subroutine f', &
517+
& 'module function g()', &
518+
& 'end function g', &
519+
& 'end module test'
520+
close(unit)
521+
522+
f_source = parse_f_source(temp_file,error)
523+
if (allocated(error)) then
524+
return
525+
end if
526+
527+
if (f_source%unit_type /= FPM_UNIT_SUBPROGRAM) then
528+
call test_failed(error,'Wrong unit type detected - expecting FPM_UNIT_SUBPROGRAM')
529+
return
530+
end if
531+
532+
if (size(f_source%modules_provided) /= 1) then
533+
call test_failed(error,'Unexpected modules_provided - expecting one')
534+
return
535+
end if
536+
537+
if (size(f_source%modules_used) /= 0) then
538+
call test_failed(error,'Incorrect number of modules_used - expecting zero')
539+
return
540+
end if
541+
542+
end subroutine test_module_with_c_api
543+
544+
492545
!> Try to parse combined fortran module and program
493546
!> Check that parsed unit type is FPM_UNIT_PROGRAM
494547
subroutine test_program_with_module(error)

0 commit comments

Comments
 (0)