I recently updated my Fortran “pathlib” library to have an object-oriented interface.
It works equally well across operating systems (Linux, macOS, Windows).
It is inspired by Python pathlib and C++ filesystem.
a small sample of “path%” methods include:
expanduser() ! expand tilde '~' to user home directory
with_suffix(new) ! replace filename suffix
as_posix() ! switch "\" for "/"
is_directory() ! directory and not a file
is_file() ! is a file and not a directory
parent() ! one level up path
I have needed similar functions ( M_path ) that complement some other modules I have; but quite a few of mine require a POSIX C interface and so do not work in several MSWindows environments. Nice. Wondering if you plan on making it available as an fpm(1) package? I particularly like the expanduser() and as_posix() which I do not have an equivalent of but already have programs where I should use them, and the interface is very intuitive. Thanks!
Looks great! In addition to the procedures you’ve already implemented I think a relative_to procedure similar to the one in pathlib would be highly appreciated.
Also, I would strongly consider making character(len=:), allocatable :: path private inside the path type. You could then implement a constructor that takes a string like this:
type :: path
private
character(len=:), allocatable :: path
end type
interface path
module procedure new_from_chars
end interface
contains
type(path) pure function new_from_chars(chars) result(p)
character(len=*), intent(in) :: chars
this%path = chars
end function
Usage:
type(path) :: p
p = path('/foo/bar')
To get the string back (e.g. for printing/opening files/etc.) you could then implement a procedure :: to_chars that takes the path object and returns a character(len=:), allocatable.
Personally I would also name the type path_t and not path so that we can have a variable type(path_t) :: path, but I don’t think there’s any consensus on such naming conventions at the moment.
EDIT: Oh and I forgot to suggest a join procedure to concatenate two paths Would also be very handy!
I don’t think fpm can do that, but you might be able to surround the submodule code with preprocessor directives like #ifdef _WIN32 etc as a workaround.
Update: now Fortran-pathlib is entirely based on C++17 filesystem. This avoids the platform-specific and compiler-specific workarounds I had to use in earlier versions of pathlib.
GCC >= 8, Clang, Intel compiler, Visual Studio, etc. have C++17 to work with the Fortran pathlib interface.
I have retained as a fallback the original pathlib C stdlib-based routines as a fallback for non-C++17 systems.
fpm is not yet supported as “build-script” and/or non-Fortran source are not yet fpm features.
I guess the current version does not yet support fpm tests (cloned your repository a few moments ago):
$ fpm test
<INFO> No tests to run
STOP 0
$ fpm --version
Version: 0.5.0, alpha
Program: fpm(1)
Description: A Fortran package manager and build system
Home Page: https://github.com/fortran-lang/fpm
License: MIT
OS Type: Linux
Yes I was incorrect, fpm is not linking in C++ fs.cpp that is needed. thus libpathlib.a is missing the functions provided by C++ with fpm. I think there are some open PRs/issues for FPM but maybe build/link C++ isn’t yet possible with FPM, without using a Makefile/script
$ fpm build
+ mkdir -p build/gfortran_2A42023B310FA28D
+ mkdir -p build/gfortran_2A42023B310FA28D/fortran-pathlib/
+ gfortran -c ./src/pathlib.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build/gfortran_2A42023B310FA28D -Ibuild/gfortran_2A42023B310FA28D -o build/gfortran_2A42023B310FA28D/fortran-pathlib/src_pathlib.f90.o
+ gfortran -c ./src/find.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build/gfortran_2A42023B310FA28D -Ibuild/gfortran_2A42023B310FA28D -o build/gfortran_2A42023B310FA28D/fortran-pathlib/src_find.f90.o
./src/find.f90:58:0:
58 | p = expanduser(path)
|
Warning: ‘.p’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/find.f90:11:0:
11 | character(:), allocatable :: path1, suff(:)
|
Warning: ‘.suff’ is used uninitialized in this function [-Wuninitialized]
./src/find.f90:27:0:
27 | get_filename = get_filename // '/' // name
|
Warning: ‘.__var_1_realloc_string’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/find.f90:37:0:
37 | path1 = get_filename
|
Warning: ‘.path1’ may be used uninitialized in this function [-Wmaybe-uninitialized]
+ gfortran -c ./src/io.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build/gfortran_2A42023B310FA28D -Ibuild/gfortran_2A42023B310FA28D -o build/gfortran_2A42023B310FA28D/fortran-pathlib/src_io.f90.o
+ gfortran -c ./src/fs_cpp.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build/gfortran_2A42023B310FA28D -Ibuild/gfortran_2A42023B310FA28D -o build/gfortran_2A42023B310FA28D/fortran-pathlib/src_fs_cpp.f90.o
./src/fs_cpp.f90:571:0:
571 | cpath = path // C_NULL_CHAR
|
Warning: ‘.cpath’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:504:0:
504 | s1 = trim(path) // C_NULL_CHAR
|
Warning: ‘.s1’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:484:0:
484 | s1 = a // C_NULL_CHAR
|
Warning: ‘.s1’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:485:0:
485 | s2 = b // C_NULL_CHAR
|
Warning: ‘.s2’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:470:0:
470 | csrc = src // C_NULL_CHAR
|
Warning: ‘.csrc’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:471:0:
471 | cdest = dest // C_NULL_CHAR
|
Warning: ‘.cdest’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:456:0:
456 | cpath = path // C_NULL_CHAR
|
Warning: ‘.cpath’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:444:0:
444 | c1 = path1 // C_NULL_CHAR
|
Warning: ‘.c1’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:445:0:
445 | c2 = path2 // C_NULL_CHAR
|
Warning: ‘.c2’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:436:0:
436 | cpath = path // C_NULL_CHAR
|
Warning: ‘.cpath’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:428:0:
428 | cpath = path // C_NULL_CHAR
|
Warning: ‘.cpath’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:421:0:
421 | cpath = path // C_NULL_CHAR
|
Warning: ‘.cpath’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:413:0:
413 | cpath = path // C_NULL_CHAR
|
Warning: ‘.cpath’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:362:0:
362 | cpath = path // C_NULL_CHAR
|
Warning: ‘.cpath’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:352:0:
352 | ctgt = tgt // C_NULL_CHAR
|
Warning: ‘.ctgt’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:353:0:
353 | clink = link // C_NULL_CHAR
|
Warning: ‘.clink’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:344:0:
344 | cpath = path // C_NULL_CHAR
|
Warning: ‘.cpath’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:336:0:
336 | cpath = path // C_NULL_CHAR
|
Warning: ‘.cpath’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/fs_cpp.f90:326:0:
326 | cpath = path // C_NULL_CHAR
|
Warning: ‘.cpath’ may be used uninitialized in this function [-Wmaybe-uninitialized]
+ gfortran -c ./src/iter.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build/gfortran_2A42023B310FA28D -Ibuild/gfortran_2A42023B310FA28D -o build/gfortran_2A42023B310FA28D/fortran-pathlib/src_iter.f90.o
./src/iter.f90:26:0:
26 | wk = as_posix(path)
|
Warning: ‘.wk’ may be used uninitialized in this function [-Wmaybe-uninitialized]
./src/iter.f90:43:0:
43 | if(wk(j:j) == "/") wk = wk(:j-1)
|
Warning: ‘.__var_1_realloc_string’ may be used uninitialized in this function [-Wmaybe-uninitialized]
+ ar -rs build/gfortran_2A42023B310FA28D/fortran-pathlib/libfortran-pathlib.a build/gfortran_2A42023B310FA28D/fortran-pathlib/src_find.f90.o build/gfortran_2A42023B310FA28D/fortran-pathlib/src_io.f90.o build/gfortran_2A42023B310FA28D/fortran-pathlib/src_fs_cpp.f90.o build/gfortran_2A42023B310FA28D/fortran-pathlib/src_iter.f90.o build/gfortran_2A42023B310FA28D/fortran-pathlib/src_pathlib.f90.o
ar: creating build/gfortran_2A42023B310FA28D/fortran-pathlib/libfortran-pathlib.a
The error message with fpm test looks more like tests are not found, not that they are found and fail.
Yes I think fpm is not recognizing that I’ve specified in fpm.toml to use src/Makefile. Fpm instead tries to build like a default fpm project, and then the test fails to link because it’s missing the fs.cpp content and c++ flags.
Using just the Makefile works from src/ directory make produces pathlib.a, which can be used like