aboutsummaryrefslogtreecommitdiff
path: root/src/fpm_sources.f90
diff options
context:
space:
mode:
Diffstat (limited to 'src/fpm_sources.f90')
-rw-r--r--src/fpm_sources.f90220
1 files changed, 220 insertions, 0 deletions
diff --git a/src/fpm_sources.f90 b/src/fpm_sources.f90
new file mode 100644
index 0000000..c781535
--- /dev/null
+++ b/src/fpm_sources.f90
@@ -0,0 +1,220 @@
+!># Discovery of sources
+!>
+!> This module implements subroutines for building a list of
+!> `[[srcfile_t]]` objects by looking for source files in the filesystem.
+!>
+module fpm_sources
+use fpm_error, only: error_t
+use fpm_model, only: srcfile_t, FPM_UNIT_PROGRAM
+use fpm_filesystem, only: basename, canon_path, dirname, join_path, list_files
+use fpm_strings, only: lower, str_ends_with, string_t, operator(.in.)
+use fpm_source_parsing, only: parse_f_source, parse_c_source
+use fpm_manifest_executable, only: executable_config_t
+implicit none
+
+private
+public :: add_sources_from_dir, add_executable_sources
+
+character(4), parameter :: fortran_suffixes(2) = [".f90", &
+ ".f "]
+
+contains
+
+!> Wrapper to source parsing routines.
+!> Selects parsing routine based on source file name extension
+function parse_source(source_file_path,error) result(source)
+ character(*), intent(in) :: source_file_path
+ type(error_t), allocatable, intent(out) :: error
+ type(srcfile_t) :: source
+
+ if (str_ends_with(lower(source_file_path), fortran_suffixes)) then
+
+ source = parse_f_source(source_file_path, error)
+
+ if (source%unit_type == FPM_UNIT_PROGRAM) then
+ source%exe_name = basename(source_file_path,suffix=.false.)
+ end if
+
+ else if (str_ends_with(lower(source_file_path), [".c", ".h"])) then
+
+ source = parse_c_source(source_file_path,error)
+
+ end if
+
+ if (allocated(error)) then
+ return
+ end if
+
+end function parse_source
+
+!> Add to `sources` by looking for source files in `directory`
+subroutine add_sources_from_dir(sources,directory,scope,with_executables,recurse,error)
+ !> List of `[[srcfile_t]]` objects to append to. Allocated if not allocated
+ type(srcfile_t), allocatable, intent(inout), target :: sources(:)
+ !> Directory in which to search for source files
+ character(*), intent(in) :: directory
+ !> Scope to apply to the discovered sources, see [[fpm_model]] for enumeration
+ integer, intent(in) :: scope
+ !> Executable sources (fortran `program`s) are ignored unless `with_executables=.true.`
+ logical, intent(in), optional :: with_executables
+ !> Whether to recursively search subdirectories, default is `.true.`
+ logical, intent(in), optional :: recurse
+ !> Error handling
+ type(error_t), allocatable, intent(out) :: error
+
+ integer :: i
+ logical, allocatable :: is_source(:), exclude_source(:)
+ type(string_t), allocatable :: file_names(:)
+ type(string_t), allocatable :: src_file_names(:)
+ type(string_t), allocatable :: existing_src_files(:)
+ type(srcfile_t), allocatable :: dir_sources(:)
+
+ ! Scan directory for sources
+ call list_files(directory, file_names,recurse=merge(recurse,.true.,present(recurse)))
+
+ if (allocated(sources)) then
+ allocate(existing_src_files(size(sources)))
+ do i=1,size(sources)
+ existing_src_files(i)%s = canon_path(sources(i)%file_name)
+ end do
+ else
+ allocate(existing_src_files(0))
+ end if
+
+ is_source = [(.not.(canon_path(file_names(i)%s) .in. existing_src_files) .and. &
+ (str_ends_with(lower(file_names(i)%s), fortran_suffixes) .or. &
+ str_ends_with(lower(file_names(i)%s),[".c",".h"]) ),i=1,size(file_names))]
+ src_file_names = pack(file_names,is_source)
+
+ allocate(dir_sources(size(src_file_names)))
+ allocate(exclude_source(size(src_file_names)))
+
+ do i = 1, size(src_file_names)
+
+ dir_sources(i) = parse_source(src_file_names(i)%s,error)
+ if (allocated(error)) return
+
+ dir_sources(i)%unit_scope = scope
+
+ ! Exclude executables unless specified otherwise
+ exclude_source(i) = (dir_sources(i)%unit_type == FPM_UNIT_PROGRAM)
+ if (dir_sources(i)%unit_type == FPM_UNIT_PROGRAM .and. &
+ & present(with_executables)) then
+ if (with_executables) then
+
+ exclude_source(i) = .false.
+
+ end if
+ end if
+
+ end do
+
+ if (.not.allocated(sources)) then
+ sources = pack(dir_sources,.not.exclude_source)
+ else
+ sources = [sources, pack(dir_sources,.not.exclude_source)]
+ end if
+
+end subroutine add_sources_from_dir
+
+
+!> Add to `sources` using the executable and test entries in the manifest and
+!> applies any executable-specific overrides such as `executable%name`.
+!> Adds all sources (including modules) from each `executable%source_dir`
+subroutine add_executable_sources(sources,executables,scope,auto_discover,error)
+ !> List of `[[srcfile_t]]` objects to append to. Allocated if not allocated
+ type(srcfile_t), allocatable, intent(inout), target :: sources(:)
+ !> List of `[[executable_config_t]]` entries from manifest
+ class(executable_config_t), intent(in) :: executables(:)
+ !> Scope to apply to the discovered sources: either `FPM_SCOPE_APP` or `FPM_SCOPE_TEST`, see [[fpm_model]]
+ integer, intent(in) :: scope
+ !> If `.false.` only executables and tests specified in the manifest are added to `sources`
+ logical, intent(in) :: auto_discover
+ !> Error handling
+ type(error_t), allocatable, intent(out) :: error
+
+ integer :: i, j
+
+ type(string_t), allocatable :: exe_dirs(:)
+ type(srcfile_t) :: exe_source
+
+ call get_executable_source_dirs(exe_dirs,executables)
+
+ do i=1,size(exe_dirs)
+ call add_sources_from_dir(sources,exe_dirs(i)%s, scope, &
+ with_executables=auto_discover, recurse=.false., error=error)
+
+ if (allocated(error)) then
+ return
+ end if
+ end do
+
+ exe_loop: do i=1,size(executables)
+
+ ! Check if executable already discovered automatically
+ ! and apply any overrides
+ do j=1,size(sources)
+
+ if (basename(sources(j)%file_name,suffix=.true.) == executables(i)%main .and.&
+ canon_path(dirname(sources(j)%file_name)) == &
+ canon_path(executables(i)%source_dir) ) then
+
+ sources(j)%exe_name = executables(i)%name
+ if (allocated(executables(i)%link)) then
+ sources(j)%link_libraries = executables(i)%link
+ end if
+ cycle exe_loop
+
+ end if
+
+ end do
+
+ ! Add if not already discovered (auto_discovery off)
+ exe_source = parse_source(join_path(executables(i)%source_dir,executables(i)%main),error)
+ exe_source%exe_name = executables(i)%name
+ if (allocated(executables(i)%link)) then
+ exe_source%link_libraries = executables(i)%link
+ end if
+ exe_source%unit_scope = scope
+
+ if (allocated(error)) return
+
+ if (.not.allocated(sources)) then
+ sources = [exe_source]
+ else
+ sources = [sources, exe_source]
+ end if
+
+ end do exe_loop
+
+end subroutine add_executable_sources
+
+!> Build a list of unique source directories
+!> from executables specified in manifest
+subroutine get_executable_source_dirs(exe_dirs,executables)
+ type(string_t), allocatable, intent(inout) :: exe_dirs(:)
+ class(executable_config_t), intent(in) :: executables(:)
+
+ type(string_t) :: dirs_temp(size(executables))
+
+ integer :: i, n
+
+ n = 0
+ do i=1,size(executables)
+ if (.not.(executables(i)%source_dir .in. dirs_temp)) then
+
+ n = n + 1
+ dirs_temp(n)%s = executables(i)%source_dir
+
+ end if
+ end do
+
+ if (.not.allocated(exe_dirs)) then
+ exe_dirs = dirs_temp(1:n)
+ else
+ exe_dirs = [exe_dirs,dirs_temp(1:n)]
+ end if
+
+end subroutine get_executable_source_dirs
+
+end module fpm_sources