Skip to content

Commit 408e96a

Browse files
authored
feat: added basic preprocess table configuration (#715)
* feat: added basic preprocess table configuration * refactor: added new_preprocessors subroutine * feat: added example for preprocess_cpp * feat: added minimal program in example for testing * fix: documentation in preprocess file * fix: Empty preprocess table should not be allowed * feat: ability to set cpp flag if preprocess.cpp is found in manifest * fix: comment message * test: added tests for preprocess table * docs: small grammatical changes * fix: added the option to modify cpp preprocessor flag * refactor: added pr usage * refactor: added checks for windows intel fortron compiler for cpp flag
1 parent 4b963be commit 408e96a

File tree

9 files changed

+349
-5
lines changed

9 files changed

+349
-5
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build/*
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
name = "preprocess_cpp"
2+
3+
[preprocess]
4+
[preprocess.cpp]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module preprocess_cpp
2+
implicit none
3+
private
4+
5+
public :: say_hello
6+
contains
7+
subroutine say_hello
8+
print *, "Hello, preprocess_cpp!"
9+
#ifndef TESTMACRO
10+
This breaks the build.
11+
#endif
12+
end subroutine say_hello
13+
end module preprocess_cpp

src/fpm.f90

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module fpm
1212
use fpm_model, only: fpm_model_t, srcfile_t, show_model, &
1313
FPM_SCOPE_UNKNOWN, FPM_SCOPE_LIB, FPM_SCOPE_DEP, &
1414
FPM_SCOPE_APP, FPM_SCOPE_EXAMPLE, FPM_SCOPE_TEST
15-
use fpm_compiler, only: new_compiler, new_archiver
15+
use fpm_compiler, only: new_compiler, new_archiver, set_preprocessor_flags
1616

1717

1818
use fpm_sources, only: add_executable_sources, add_sources_from_dir
@@ -77,6 +77,9 @@ subroutine build_model(model, settings, package, error)
7777
flags = flags // model%compiler%get_default_flags(settings%profile == "release")
7878
end select
7979
end if
80+
81+
call set_preprocessor_flags(model%compiler%id, flags)
82+
8083
cflags = trim(settings%cflag)
8184
ldflags = trim(settings%ldflag)
8285

src/fpm/manifest.f90

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module fpm_manifest
1212
use fpm_manifest_executable, only : executable_config_t
1313
use fpm_manifest_dependency, only : dependency_config_t
1414
use fpm_manifest_library, only : library_config_t
15+
use fpm_mainfest_preprocess, only : preprocess_config_t
1516
use fpm_manifest_package, only : package_config_t, new_package
1617
use fpm_error, only : error_t, fatal_error, file_not_found_error
1718
use fpm_toml, only : toml_table, read_package_file
@@ -23,7 +24,7 @@ module fpm_manifest
2324

2425
public :: get_package_data, default_executable, default_library, default_test
2526
public :: default_example
26-
public :: package_config_t, dependency_config_t
27+
public :: package_config_t, dependency_config_t, preprocess_config_t
2728

2829

2930
contains

src/fpm/manifest/package.f90

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ module fpm_manifest_package
3939
use fpm_manifest_library, only : library_config_t, new_library
4040
use fpm_manifest_install, only: install_config_t, new_install_config
4141
use fpm_manifest_test, only : test_config_t, new_test
42+
use fpm_mainfest_preprocess, only : preprocess_config_t, new_preprocessors
4243
use fpm_filesystem, only : exists, getline, join_path
4344
use fpm_error, only : error_t, fatal_error, syntax_error, bad_name_error
4445
use fpm_toml, only : toml_table, toml_array, toml_key, toml_stat, get_value, &
@@ -89,6 +90,9 @@ module fpm_manifest_package
8990
!> Test meta data
9091
type(test_config_t), allocatable :: test(:)
9192

93+
!> Preprocess meta data
94+
type(preprocess_config_t), allocatable :: preprocess(:)
95+
9296
contains
9397

9498
!> Print information on this instance
@@ -267,6 +271,11 @@ subroutine new_package(self, table, root, error)
267271
if (allocated(error)) return
268272
end if
269273

274+
call get_value(table, "preprocess", child, requested=.false.)
275+
if (associated(child)) then
276+
call new_preprocessors(self%preprocess, child, error)
277+
if (allocated(error)) return
278+
end if
270279
end subroutine new_package
271280

272281

@@ -304,7 +313,7 @@ subroutine check(table, error)
304313
case("version", "license", "author", "maintainer", "copyright", &
305314
& "description", "keywords", "categories", "homepage", "build", &
306315
& "dependencies", "dev-dependencies", "test", "executable", &
307-
& "example", "library", "install", "extra")
316+
& "example", "library", "install", "extra", "preprocess")
308317
continue
309318

310319
end select

src/fpm/manifest/preprocess.f90

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
!> Implementation of the meta data for preprocessing.
2+
!>
3+
!> A preprocess table can currently have the following fields
4+
!>
5+
!> ```toml
6+
!> [preprocess]
7+
!> [preprocess.cpp]
8+
!> suffixes = ["F90", "f90"]
9+
!> directories = ["src/feature1", "src/models"]
10+
!> macros = []
11+
!> ```
12+
13+
module fpm_mainfest_preprocess
14+
use fpm_error, only : error_t, syntax_error
15+
use fpm_strings, only : string_t
16+
use fpm_toml, only : toml_table, toml_key, toml_stat, get_value, get_list
17+
implicit none
18+
private
19+
20+
public :: preprocess_config_t, new_preprocess_config, new_preprocessors
21+
22+
!> Configuration meta data for a preprocessor
23+
type :: preprocess_config_t
24+
25+
!> Name of the preprocessor
26+
character(len=:), allocatable :: name
27+
28+
!> Suffixes of the files to be preprocessed
29+
type(string_t), allocatable :: suffixes(:)
30+
31+
!> Directories to search for files to be preprocessed
32+
type(string_t), allocatable :: directories(:)
33+
34+
!> Macros to be defined for the preprocessor
35+
type(string_t), allocatable :: macros(:)
36+
37+
contains
38+
39+
!> Print information on this instance
40+
procedure :: info
41+
42+
end type preprocess_config_t
43+
44+
contains
45+
46+
!> Construct a new preprocess configuration from TOML data structure
47+
subroutine new_preprocess_config(self, table, error)
48+
49+
!> Instance of the preprocess configuration
50+
type(preprocess_config_t), intent(out) :: self
51+
52+
!> Instance of the TOML data structure.
53+
type(toml_table), intent(inout) :: table
54+
55+
!> Error handling
56+
type(error_t), allocatable, intent(inout) :: error
57+
58+
call check(table, error)
59+
if (allocated(error)) return
60+
61+
call table%get_key(self%name)
62+
63+
call get_list(table, "suffixes", self%suffixes, error)
64+
if (allocated(error)) return
65+
66+
call get_list(table, "directories", self%directories, error)
67+
if (allocated(error)) return
68+
69+
call get_list(table, "macros", self%macros, error)
70+
if (allocated(error)) return
71+
72+
end subroutine new_preprocess_config
73+
74+
!> Check local schema for allowed entries
75+
subroutine check(table, error)
76+
77+
!> Instance of the TOML data structure.
78+
type(toml_table), intent(inout) :: table
79+
80+
!> Error handling
81+
type(error_t), allocatable, intent(inout) :: error
82+
83+
character(len=:), allocatable :: name
84+
type(toml_key), allocatable :: list(:)
85+
logical :: suffixes_present, directories_present, macros_present
86+
integer :: ikey
87+
88+
suffixes_present = .false.
89+
directories_present = .false.
90+
macros_present = .false.
91+
92+
call table%get_key(name)
93+
call table%get_keys(list)
94+
95+
do ikey = 1, size(list)
96+
select case(list(ikey)%key)
97+
case default
98+
call syntax_error(error, "Key " // list(ikey)%key // "is not allowed in preprocessor"//name)
99+
exit
100+
case("suffixes")
101+
suffixes_present = .true.
102+
case("directories")
103+
directories_present = .true.
104+
case("macros")
105+
macros_present = .true.
106+
end select
107+
end do
108+
end subroutine check
109+
110+
!> Construct new preprocess array from a TOML data structure.
111+
subroutine new_preprocessors(preprocessors, table, error)
112+
113+
!> Instance of the preprocess configuration
114+
type(preprocess_config_t), allocatable, intent(out) :: preprocessors(:)
115+
116+
!> Instance of the TOML data structure
117+
type(toml_table), intent(inout) :: table
118+
119+
!> Error handling
120+
type(error_t), allocatable, intent(out) :: error
121+
122+
type(toml_table), pointer :: node
123+
type(toml_key), allocatable :: list(:)
124+
integer :: iprep, stat
125+
126+
call table%get_keys(list)
127+
128+
! An empty table is not allowed
129+
if (size(list) == 0) then
130+
call syntax_error(error, "No preprocessors defined")
131+
end if
132+
133+
allocate(preprocessors(size(list)))
134+
do iprep = 1, size(list)
135+
call get_value(table, list(iprep)%key, node, stat=stat)
136+
if (stat /= toml_stat%success) then
137+
call syntax_error(error, "Preprocessor "//list(iprep)%key//" must be a table entry")
138+
exit
139+
end if
140+
call new_preprocess_config(preprocessors(iprep), node, error)
141+
if (allocated(error)) exit
142+
end do
143+
144+
end subroutine new_preprocessors
145+
146+
!> Write information on this instance
147+
subroutine info(self, unit, verbosity)
148+
149+
!> Instance of the preprocess configuration
150+
class(preprocess_config_t), intent(in) :: self
151+
152+
!> Unit for IO
153+
integer, intent(in) :: unit
154+
155+
!> Verbosity of the printout
156+
integer, intent(in), optional :: verbosity
157+
158+
integer :: pr, ilink
159+
character(len=*), parameter :: fmt = '("#", 1x, a, t30, a)'
160+
161+
if (present(verbosity)) then
162+
pr = verbosity
163+
else
164+
pr = 1
165+
end if
166+
167+
if (pr < 1) return
168+
169+
write(unit, fmt) "Preprocessor"
170+
if (allocated(self%name)) then
171+
write(unit, fmt) "- name", self%name
172+
end if
173+
if (allocated(self%suffixes)) then
174+
write(unit, fmt) " - suffixes"
175+
do ilink = 1, size(self%suffixes)
176+
write(unit, fmt) " - " // self%suffixes(ilink)%s
177+
end do
178+
end if
179+
if (allocated(self%directories)) then
180+
write(unit, fmt) " - directories"
181+
do ilink = 1, size(self%directories)
182+
write(unit, fmt) " - " // self%directories(ilink)%s
183+
end do
184+
end if
185+
if (allocated(self%macros)) then
186+
write(unit, fmt) " - macros"
187+
do ilink = 1, size(self%macros)
188+
write(unit, fmt) " - " // self%macros(ilink)%s
189+
end do
190+
end if
191+
192+
end subroutine info
193+
194+
end module fpm_mainfest_preprocess

src/fpm_compiler.f90

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ module fpm_compiler
4040
use fpm_filesystem, only: join_path, basename, get_temp_filename, delete_file, unix_path, &
4141
& getline, run
4242
use fpm_strings, only: split, string_cat, string_t
43+
use fpm_manifest, only : get_package_data, package_config_t
44+
use fpm_error, only: error_t
4345
implicit none
4446
public :: compiler_t, new_compiler, archiver_t, new_archiver
4547
public :: debug
@@ -177,6 +179,7 @@ module fpm_compiler
177179
character(*), parameter :: &
178180
flag_lfortran_opt = " --fast"
179181

182+
180183
contains
181184

182185

@@ -376,6 +379,49 @@ subroutine get_debug_compile_flags(id, flags)
376379
end select
377380
end subroutine get_debug_compile_flags
378381

382+
subroutine set_preprocessor_flags (id, flags)
383+
integer(compiler_enum), intent(in) :: id
384+
type(package_config_t) :: package
385+
type(error_t), allocatable :: error
386+
character(len=:), allocatable :: flags
387+
character(len=:), allocatable :: flag_cpp_preprocessor
388+
389+
integer :: i
390+
391+
call get_package_data(package, "fpm.toml", error)
392+
393+
if (allocated(error)) then
394+
return
395+
end if
396+
397+
!> Check if there is a preprocess table
398+
if (.not.allocated(package%preprocess)) then
399+
return
400+
end if
401+
402+
!> Modify the flag_cpp_preprocessor on the basis of the compiler.
403+
select case(id)
404+
case default
405+
flag_cpp_preprocessor = ""
406+
case(id_caf, id_gcc, id_f95, id_nvhpc)
407+
flag_cpp_preprocessor = "-cpp"
408+
case(id_intel_classic_windows, id_intel_llvm_windows)
409+
flag_cpp_preprocessor = "/fpp"
410+
case(id_intel_classic_nix, id_intel_classic_mac, id_intel_llvm_nix, id_nag)
411+
flag_cpp_preprocessor = "-fpp"
412+
case(id_lfortran)
413+
flag_cpp_preprocessor = "--cpp"
414+
end select
415+
416+
do i = 1, size(package%preprocess)
417+
if (package%preprocess(i)%name == "cpp") then
418+
flags = flag_cpp_preprocessor// flags
419+
exit
420+
end if
421+
end do
422+
423+
end subroutine set_preprocessor_flags
424+
379425
function get_include_flag(self, path) result(flags)
380426
class(compiler_t), intent(in) :: self
381427
character(len=*), intent(in) :: path

0 commit comments

Comments
 (0)