Skip to content

Commit 2403309

Browse files
committed
Implement interface to TOML-Fortran and reading of fpm.toml
1 parent 39fb22d commit 2403309

11 files changed

+1303
-23
lines changed

fpm/fpm.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ license = "MIT"
44
author = "fpm maintainers"
55
maintainer = ""
66
copyright = "2020 fpm contributors"
7+
8+
[dependencies]
9+
toml-f = { git = "https://github.com/toml-f/toml-f" }

fpm/src/fpm.f90

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
module fpm
22
use environment, only: get_os_type, OS_LINUX, OS_MACOS, OS_WINDOWS
3+
use fpm_config, only : get_package_data, package_t
4+
use fpm_error, only : error_t
35
implicit none
46
private
57
public :: cmd_build, cmd_install, cmd_new, cmd_run, cmd_test
@@ -85,34 +87,53 @@ logical function str_ends_with(s, e) result(r)
8587
end if
8688
end function
8789

88-
subroutine package_name(name)
89-
character(:), allocatable, intent(out) :: name
90-
! Currrently a heuristic. We should update this to read the name from fpm.toml
91-
if (exists("src/fpm.f90")) then
92-
name = "fpm"
93-
else
94-
name = "hello_world"
95-
end if
96-
end subroutine
97-
9890
subroutine cmd_build()
91+
type(package_t) :: package
92+
type(error_t), allocatable :: error
9993
type(string_t), allocatable :: files(:)
100-
character(:), allocatable :: basename, pkg_name, linking
94+
character(:), allocatable :: basename, linking
10195
integer :: i, n
102-
print *, "# Building project"
103-
call list_files("src", files)
96+
call get_package_data(package, "fpm.toml", error)
97+
if (allocated(error)) then
98+
print '(a)', error%message
99+
error stop 1
100+
end if
101+
102+
! Populate library in case we find the default src directory
103+
if (.not.allocated(package%library) .and. exists("src")) then
104+
allocate(package%library)
105+
package%library%source_dir = "src"
106+
end if
107+
108+
! Populate executable in case we find the default app directory
109+
if (.not.allocated(package%executable) .and. exists("app")) then
110+
allocate(package%executable(1))
111+
package%executable(1)%name = package%name
112+
package%executable(1)%source_dir = "app"
113+
package%executable(1)%main = "main.f90"
114+
end if
115+
104116
linking = ""
105-
do i = 1, size(files)
106-
if (str_ends_with(files(i)%s, ".f90")) then
107-
n = len(files(i)%s)
108-
basename = files(i)%s(1:n-4)
109-
call run("gfortran -c src/" // basename // ".f90 -o " // basename // ".o")
110-
linking = linking // " " // basename // ".o"
111-
end if
117+
if (allocated(package%library)) then
118+
call list_files(package%library%source_dir, files)
119+
do i = 1, size(files)
120+
if (str_ends_with(files(i)%s, ".f90")) then
121+
n = len(files(i)%s)
122+
basename = files(i)%s
123+
call run("gfortran -c " // package%library%source_dir // "/" // &
124+
& basename // " -o " // basename // ".o")
125+
linking = linking // " " // basename // ".o"
126+
end if
127+
end do
128+
end if
129+
130+
do i = 1, size(package%executable)
131+
basename = package%executable(i)%main
132+
call run("gfortran -c " // package%executable(i)%source_dir // "/" // &
133+
& basename // " -o " // basename // ".o")
134+
call run("gfortran " // basename // ".o " // linking // " -o " // &
135+
& package%executable(i)%name)
112136
end do
113-
call run("gfortran -c app/main.f90 -o main.o")
114-
call package_name(pkg_name)
115-
call run("gfortran main.o " // linking // " -o " // pkg_name)
116137
end subroutine
117138

118139
subroutine cmd_install()

fpm/src/fpm_config.f90

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
!> Package configuration data
2+
module fpm_config
3+
use fpm_config_package, only : package_t, new_package
4+
use fpm_error, only : error_t, fatal_error, file_not_found_error
5+
use fpm_toml, only : toml_table, read_package_file
6+
implicit none
7+
private
8+
9+
public :: get_package_data
10+
public :: package_t
11+
12+
13+
contains
14+
15+
16+
!> Obtain package meta data from an configuation file
17+
subroutine get_package_data(package, file, error)
18+
19+
!> Parsed package meta data
20+
type(package_t), intent(out) :: package
21+
22+
!> Name of the package configuration file
23+
character(len=*), intent(in) :: file
24+
25+
!> Error status of the operation
26+
type(error_t), allocatable, intent(out) :: error
27+
28+
type(toml_table), allocatable :: table
29+
30+
call read_package_file(table, file, error)
31+
if (allocated(error)) return
32+
33+
if (.not.allocated(table)) then
34+
call fatal_error(error, "Unclassified error while reading: '"//file//"'")
35+
return
36+
end if
37+
38+
call new_package(package, table, error)
39+
40+
end subroutine get_package_data
41+
42+
43+
end module fpm_config

fpm/src/fpm_config_dependency.f90

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
!> Implementation of the meta data for dependencies.
2+
!
3+
! A dependency table can currently have the following fields
4+
!
5+
! ```toml
6+
! [dependencies]
7+
! "dep1" = { git = "url" }
8+
! "dep2" = { git = "url", branch = "name" }
9+
! "dep3" = { git = "url", tag = "name" }
10+
! "dep4" = { git = "url", rev = "sha1" }
11+
! "dep0" = { path = "path" }
12+
! ```
13+
module fpm_config_dependency
14+
use fpm_error, only : error_t, syntax_error
15+
use fpm_git, only : git_target_t, git_target_tag, git_target_branch, &
16+
& git_target_revision, git_target_default
17+
use fpm_toml, only : toml_table, toml_key, toml_stat, get_value
18+
implicit none
19+
private
20+
21+
public :: dependency_t, new_dependency, new_dependencies
22+
23+
24+
!> Configuration meta data for a dependency
25+
type :: dependency_t
26+
27+
!> Name of the dependency
28+
character(len=:), allocatable :: name
29+
30+
!> Local target
31+
character(len=:), allocatable :: path
32+
33+
!> Git descriptor
34+
type(git_target_t), allocatable :: git
35+
36+
contains
37+
38+
!> Print information on this instance
39+
procedure :: info
40+
41+
end type dependency_t
42+
43+
44+
contains
45+
46+
47+
!> Construct a new dependency configuration from a TOML data structure
48+
subroutine new_dependency(self, table, error)
49+
50+
!> Instance of the dependency configuration
51+
type(dependency_t), intent(out) :: self
52+
53+
!> Instance of the TOML data structure
54+
type(toml_table), intent(inout) :: table
55+
56+
!> Error handling
57+
type(error_t), allocatable, intent(out) :: error
58+
59+
character(len=:), allocatable :: url, obj
60+
integer :: stat
61+
62+
call check(table, error)
63+
if (allocated(error)) return
64+
65+
call table%get_key(self%name)
66+
67+
call get_value(table, "path", url)
68+
if (allocated(url)) then
69+
call move_alloc(url, self%path)
70+
else
71+
call get_value(table, "git", url)
72+
73+
call get_value(table, "tag", obj)
74+
if (allocated(obj)) then
75+
self%git = git_target_tag(url, obj)
76+
end if
77+
78+
if (.not.allocated(self%git)) then
79+
call get_value(table, "branch", obj)
80+
if (allocated(obj)) then
81+
self%git = git_target_branch(url, obj)
82+
end if
83+
end if
84+
85+
if (.not.allocated(self%git)) then
86+
call get_value(table, "revision", obj)
87+
if (allocated(obj)) then
88+
self%git = git_target_revision(url, obj)
89+
end if
90+
end if
91+
92+
if (.not.allocated(self%git)) then
93+
self%git = git_target_default(url)
94+
end if
95+
96+
end if
97+
98+
end subroutine new_dependency
99+
100+
101+
!> Check local schema for allowed entries
102+
subroutine check(table, error)
103+
104+
!> Instance of the TOML data structure
105+
type(toml_table), intent(inout) :: table
106+
107+
!> Error handling
108+
type(error_t), allocatable, intent(out) :: error
109+
110+
character(len=:), allocatable :: name
111+
type(toml_key), allocatable :: list(:)
112+
logical :: url_present, git_target_present
113+
integer :: ikey
114+
115+
url_present = .false.
116+
git_target_present = .false.
117+
118+
call table%get_key(name)
119+
call table%get_keys(list)
120+
121+
if (.not.allocated(list)) then
122+
call syntax_error(error, "Dependency "//name//" does not provide sufficient entries")
123+
return
124+
end if
125+
126+
do ikey = 1, size(list)
127+
select case(list(ikey)%key)
128+
case default
129+
call syntax_error(error, "Key "//list(ikey)%key//" is not allowed in dependency "//name)
130+
exit
131+
132+
case("git", "path")
133+
if (url_present) then
134+
call syntax_error(error, "Dependency "//name//" cannot have both git and path entries")
135+
exit
136+
end if
137+
url_present = .true.
138+
139+
case("branch", "rev", "tag")
140+
if (git_target_present) then
141+
call syntax_error(error, "Dependency "//name//" can only have one of branch, rev or tag present")
142+
exit
143+
end if
144+
git_target_present = .true.
145+
146+
end select
147+
end do
148+
if (allocated(error)) return
149+
150+
if (.not.url_present .and. git_target_present) then
151+
call syntax_error(error, "Dependency "//name//" uses a local path, therefore no git identifiers are allowed")
152+
end if
153+
154+
end subroutine check
155+
156+
157+
!> Construct new dependency array from a TOML data structure
158+
subroutine new_dependencies(deps, table, error)
159+
160+
!> Instance of the dependency configuration
161+
type(dependency_t), allocatable, intent(out) :: deps(:)
162+
163+
!> Instance of the TOML data structure
164+
type(toml_table), intent(inout) :: table
165+
166+
!> Error handling
167+
type(error_t), allocatable, intent(out) :: error
168+
169+
class(toml_table), pointer :: node
170+
type(toml_key), allocatable :: list(:)
171+
integer :: idep, stat
172+
173+
call table%get_keys(list)
174+
! An empty table is okay
175+
if (.not.allocated(list)) return
176+
177+
allocate(deps(size(list)))
178+
do idep = 1, size(list)
179+
call get_value(table, list(idep)%key, node, stat=stat)
180+
if (stat /= toml_stat%success) then
181+
call syntax_error(error, "Dependency "//list(idep)%key//" must be a table entry")
182+
exit
183+
end if
184+
call new_dependency(deps(idep), node, error)
185+
if (allocated(error)) exit
186+
end do
187+
188+
end subroutine new_dependencies
189+
190+
191+
!> Write information on instance
192+
subroutine info(self, unit, verbosity)
193+
194+
!> Instance of the dependency configuration
195+
class(dependency_t), intent(in) :: self
196+
197+
!> Unit for IO
198+
integer, intent(in) :: unit
199+
200+
!> Verbosity of the printout
201+
integer, intent(in), optional :: verbosity
202+
203+
integer :: pr
204+
character(len=*), parameter :: fmt = '("#", 1x, a, t30, a)'
205+
206+
if (present(verbosity)) then
207+
pr = verbosity
208+
else
209+
pr = 1
210+
end if
211+
212+
write(unit, fmt) "Dependency"
213+
if (allocated(self%name)) then
214+
write(unit, fmt) "- name", self%name
215+
end if
216+
217+
if (allocated(self%git)) then
218+
write(unit, fmt) "- kind", "git"
219+
call self%git%info(unit, pr - 1)
220+
end if
221+
222+
if (allocated(self%path)) then
223+
write(unit, fmt) "- kind", "local"
224+
write(unit, fmt) "- path", self%path
225+
end if
226+
227+
end subroutine info
228+
229+
230+
end module fpm_config_dependency

0 commit comments

Comments
 (0)