@@ -3,9 +3,11 @@ module fpm_os
33 use fpm_filesystem, only: exists, join_path, get_home
44 use fpm_environment, only: os_is_unix
55 use fpm_error, only: error_t, fatal_error
6+
67 implicit none
78 private
8- public :: change_directory, get_current_directory, get_absolute_path, convert_to_absolute_path
9+ public :: change_directory, get_current_directory, get_absolute_path, convert_to_absolute_path, &
10+ & get_absolute_path_by_cd
911
1012 integer (c_int), parameter :: buffersize = 1000_c_int
1113
@@ -47,8 +49,18 @@ function realpath(path, resolved_path) result(ptr) bind(C)
4749 type (c_ptr) :: ptr
4850 end function realpath
4951
52+ ! > Determine the absolute, canonicalized path for a given path. Windows-only.
53+ function fullpath (resolved_path , path , maxLength ) result(ptr) bind(C, name= " _fullpath" )
54+ import :: c_ptr, c_char, c_int
55+ character (kind= c_char, len= 1 ), intent (in ) :: path(* )
56+ character (kind= c_char, len= 1 ), intent (out ) :: resolved_path(* )
57+ integer (c_int), value, intent (in ) :: maxLength
58+ type (c_ptr) :: ptr
59+ end function fullpath
60+
5061 ! > Determine the absolute, canonicalized path for a given path.
51- ! > Calls custom C routine and is able to distinguish between Unix and Windows.
62+ ! > Calls custom C routine because the `_WIN32` macro is correctly exported
63+ ! > in C using `gfortran`.
5264 function c_realpath (path , resolved_path , maxLength ) result(ptr) &
5365 bind(C, name= " c_realpath" )
5466 import :: c_ptr, c_char, c_int
@@ -126,6 +138,10 @@ subroutine c_f_character(rhs, lhs)
126138 end subroutine c_f_character
127139
128140 ! > Determine the canonical, absolute path for the given path.
141+ ! >
142+ ! > Calls a C routine that uses the `_WIN32` macro to determine the correct function.
143+ ! >
144+ ! > Cannot be used in bootstrap mode.
129145 subroutine get_realpath (path , real_path , error )
130146 character (len=* ), intent (in ) :: path
131147 character (len= :), allocatable , intent (out ) :: real_path
@@ -145,10 +161,7 @@ subroutine get_realpath(path, real_path, error)
145161
146162 allocate (cpath(buffersize))
147163
148- ! The _WIN32 macro is currently not exported using gfortran.
149- #if defined(FPM_BOOTSTRAP) && !defined(_WIN32)
150- ptr = realpath(appended_path, cpath)
151- #else
164+ #ifndef FPM_BOOTSTRAP
152165 ptr = c_realpath(appended_path, cpath, buffersize)
153166#endif
154167
@@ -158,7 +171,7 @@ subroutine get_realpath(path, real_path, error)
158171 call fatal_error(error, " Failed to retrieve absolute path for '" // path// " '." )
159172 end if
160173
161- end subroutine get_realpath
174+ end subroutine
162175
163176 ! > Determine the canonical, absolute path for the given path.
164177 ! > Expands home folder (~) on both Unix and Windows.
@@ -169,49 +182,66 @@ subroutine get_absolute_path(path, absolute_path, error)
169182
170183 character (len= :), allocatable :: home
171184
185+ #ifdef FPM_BOOTSTRAP
186+ call get_absolute_path_by_cd(path, absolute_path, error); return
187+ #endif
188+
172189 if (len_trim (path) < 1 ) then
173- ! Empty path
174- call fatal_error(error, ' Path cannot be empty' )
175- return
190+ call fatal_error(error, ' Path cannot be empty' ); return
176191 else if (path(1 :1 ) == ' ~' ) then
177- ! Expand home
178192 call get_home(home, error)
179193 if (allocated (error)) return
180194
181195 if (len_trim (path) == 1 ) then
182- absolute_path = home
183- return
196+ absolute_path = home; return
184197 end if
185198
186199 if (os_is_unix()) then
187200 if (path(2 :2 ) /= ' /' ) then
188- call fatal_error(error, " Wrong separator in path: '" // path// " '" )
189- return
201+ call fatal_error(error, " Wrong separator in path: '" // path// " '" ); return
190202 end if
191203 else
192204 if (path(2 :2 ) /= ' \' ) then
193- call fatal_error(error, " Wrong separator in path: '" // path// " '" )
194- return
205+ call fatal_error(error, " Wrong separator in path: '" // path// " '" ); return
195206 end if
196207 end if
197208
198209 if (len_trim (path) == 2 ) then
199- absolute_path = home
200- return
210+ absolute_path = home; return
201211 end if
202212
203213 absolute_path = join_path(home, path(3 :len_trim (path)))
204214
205215 if (.not. exists(absolute_path)) then
206- call fatal_error(error, " Path not found: '" // absolute_path// " '" )
207- deallocate (absolute_path)
208- return
216+ call fatal_error(error, " Path not found: '" // absolute_path// " '" ); return
209217 end if
210218 else
211219 ! Get canonicalized absolute path from either the absolute or the relative path.
212220 call get_realpath(path, absolute_path, error)
213221 end if
222+ end subroutine
223+
224+ ! > Alternative to `get_absolute_path` that uses `chdir`/`_chdir` to determine the absolute path.
225+ ! >
226+ ! > `get_absolute_path` is preferred but `get_absolute_path_by_cd` can be used in bootstrap mode.
227+ subroutine get_absolute_path_by_cd (path , absolute_path , error )
228+ character (len=* ), intent (in ) :: path
229+ character (len= :), allocatable , intent (out ) :: absolute_path
230+ type (error_t), allocatable , intent (out ) :: error
231+
232+ character (len= :), allocatable :: current_path
233+
234+ call get_current_directory(current_path, error)
235+ if (allocated (error)) return
236+
237+ call change_directory(path, error)
238+ if (allocated (error)) return
239+
240+ call get_current_directory(absolute_path, error)
241+ if (allocated (error)) return
214242
243+ call change_directory(current_path, error)
244+ if (allocated (error)) return
215245 end subroutine
216246
217247 ! > Converts a path to an absolute, canonical path.
0 commit comments