compute_watch_files_from_settings Subroutine

public subroutine compute_watch_files_from_settings(settings, w, files, file_mask, roots, manifest_key, build_secs, secs_model, secs_targets, secs_watch)

Compute watched files and masks from current fpm settings.

This is the primary "graph to watchlist" entry point.

Outputs

  • files: normalized, unique file paths to watch.
  • file_mask: a bitmask per file that encodes which root target(s) the file belongs to (used for name injection on run/test).
  • roots: root executable/test targets. roots(r)%mask is a 1-bit mask.
  • manifest_key: placeholder output (computed elsewhere); set to 0 here.
  • build_secs: sum of secs_model + secs_targets + secs_watch.
  • secs_model: time spent building the fpm model.
  • secs_targets: time spent expanding sources into targets.
  • secs_watch: time spent collecting and filtering file paths.

Notes

  • Root bitmasks are limited to bit_size(0_int64) (typically 64 roots).
  • Build directory paths are excluded from the watch list unless dependency watching is enabled and the path is identified as belonging to a dep.

Arguments

Type IntentOptional Attributes Name
class(fpm_build_settings), intent(inout) :: settings
type(watch_opts_t), intent(in) :: w
type(string_t), intent(out), allocatable :: files(:)
integer(kind=int64), intent(out), allocatable :: file_mask(:)
type(root_info_t), intent(out), allocatable :: roots(:)
integer(kind=int64), intent(out) :: manifest_key
real, intent(out) :: build_secs
real, intent(out) :: secs_model
real, intent(out) :: secs_targets
real, intent(out) :: secs_watch

Calls

proc~~compute_watch_files_from_settings~~CallsGraph proc~compute_watch_files_from_settings compute_watch_files_from_settings add add proc~compute_watch_files_from_settings->add basename basename proc~compute_watch_files_from_settings->basename build_model build_model proc~compute_watch_files_from_settings->build_model dep dep proc~compute_watch_files_from_settings->dep get_package_data get_package_data proc~compute_watch_files_from_settings->get_package_data is_executable_target is_executable_target proc~compute_watch_files_from_settings->is_executable_target join_path join_path proc~compute_watch_files_from_settings->join_path new_dependency_tree new_dependency_tree proc~compute_watch_files_from_settings->new_dependency_tree proc~dfs_mark_bool dfs_mark_bool proc~compute_watch_files_from_settings->proc~dfs_mark_bool proc~dfs_mark_mask dfs_mark_mask proc~compute_watch_files_from_settings->proc~dfs_mark_mask proc~filter_watch_files filter_watch_files proc~compute_watch_files_from_settings->proc~filter_watch_files proc~ftoa ftoa proc~compute_watch_files_from_settings->proc~ftoa proc~gather_files_with_mask gather_files_with_mask proc~compute_watch_files_from_settings->proc~gather_files_with_mask proc~is_run_or_test is_run_or_test proc~compute_watch_files_from_settings->proc~is_run_or_test proc~log_info log_info proc~compute_watch_files_from_settings->proc~log_info proc~normalize_path normalize_path proc~compute_watch_files_from_settings->proc~normalize_path proc~push_file_with_mask push_file_with_mask proc~compute_watch_files_from_settings->proc~push_file_with_mask proc~select_names_or_all select_names_or_all proc~compute_watch_files_from_settings->proc~select_names_or_all proc~trim_or_empty trim_or_empty proc~compute_watch_files_from_settings->proc~trim_or_empty str str proc~compute_watch_files_from_settings->str targets_from_sources targets_from_sources proc~compute_watch_files_from_settings->targets_from_sources proc~dfs_mark_bool->proc~dfs_mark_bool dependencies dependencies proc~dfs_mark_bool->dependencies proc~find_ptr_index find_ptr_index proc~dfs_mark_bool->proc~find_ptr_index proc~dfs_mark_mask->proc~dfs_mark_mask proc~dfs_mark_mask->dependencies proc~dfs_mark_mask->proc~find_ptr_index proc~filter_watch_files->basename proc~filter_watch_files->proc~normalize_path glob glob proc~filter_watch_files->glob include_dependencies include_dependencies proc~gather_files_with_mask->include_dependencies proc~vec_push_unique vec_push_unique proc~gather_files_with_mask->proc~vec_push_unique colorize colorize proc~log_info->colorize proc~push_file_with_mask->proc~vec_push_unique proc~select_names_or_all->basename proc~select_names_or_all->is_executable_target proc~select_names_or_all->glob proc~vec_push_unique->proc~normalize_path exists exists proc~vec_push_unique->exists proc~is_ignored_path is_ignored_path proc~vec_push_unique->proc~is_ignored_path proc~is_in_dep_dirs is_in_dep_dirs proc~vec_push_unique->proc~is_in_dep_dirs proc~vec_grow vec_grow proc~vec_push_unique->proc~vec_grow proc~is_ignored_path->proc~normalize_path proc~contains_path_fragment contains_path_fragment proc~is_ignored_path->proc~contains_path_fragment proc~starts_with starts_with proc~is_ignored_path->proc~starts_with proc~is_in_dep_dirs->proc~normalize_path proc~is_in_dep_dirs->proc~starts_with proc~ends_with ends_with proc~contains_path_fragment->proc~ends_with

Called by

proc~~compute_watch_files_from_settings~~CalledByGraph proc~compute_watch_files_from_settings compute_watch_files_from_settings proc~rebuild_watch_list rebuild_watch_list proc~rebuild_watch_list->proc~compute_watch_files_from_settings proc~handle_manifest_change handle_manifest_change proc~handle_manifest_change->proc~rebuild_watch_list proc~watcher_init watcher_t%watcher_init proc~watcher_init->proc~rebuild_watch_list proc~watcher_run watcher_t%watcher_run proc~watcher_run->proc~rebuild_watch_list proc~watcher_run->proc~handle_manifest_change

Source Code

   subroutine compute_watch_files_from_settings(settings, w, files, file_mask, roots, manifest_key, build_secs, secs_model, secs_targets, secs_watch)
      class(fpm_build_settings), intent(inout) :: settings
      type(watch_opts_t), intent(in) :: w
      type(string_t), allocatable, intent(out) :: files(:)
      integer(int64), allocatable, intent(out) :: file_mask(:)
      type(root_info_t), allocatable, intent(out) :: roots(:)
      integer(int64), intent(out) :: manifest_key
      real, intent(out) :: build_secs
      real, intent(out) :: secs_model, secs_targets, secs_watch

      type(package_config_t), allocatable :: package
      type(fpm_model_t),       allocatable :: model
      type(build_target_ptr),  allocatable :: targets(:)
      type(error_t), allocatable :: err
      type(dependency_tree_t) :: tree

      logical, allocatable :: keep(:)
      logical, allocatable :: visited(:)
      integer(int64), allocatable :: target_mask(:)

      type(string_t), allocatable :: dep_dirs(:)

      integer :: i, r, nroots
      integer(int64) :: rate, t0, t1, tw0, tw1
      integer :: n_before, n_after

      secs_model   = 0.0
      secs_targets = 0.0
      secs_watch   = 0.0
      build_secs   = 0.0

      call system_clock(count_rate=rate)
      if (rate <= 0_int64) rate = 1000_int64

      allocate(package, model)

      call get_package_data(package, "fpm.toml", err, apply_defaults=.true.)
      if (allocated(err)) then
         write(error_unit,'(a)') "fpm-watch: manifest error: " // err%message
         stop 1
      end if

      call new_dependency_tree(tree, build_dir=trim_or_empty(settings%build_dir), path_to_config=trim_or_empty(settings%path_to_config))

      call tree%add(package, err)
      if (allocated(err)) then
         write(error_unit,'(a)') "fpm-watch: dependency error: " // err%message
         stop 1
      end if

      if (w%watch_deps) then
         call collect_dep_dirs(tree, dep_dirs)
         if (w%verbosity >= 1) then
            call log_info(w, "deps: watching dependency sources (deps=" // str(size(dep_dirs)) // ")")
         end if
      else
         if (allocated(dep_dirs)) deallocate(dep_dirs)
         allocate(dep_dirs(0))
      end if

      call system_clock(t0)
      call build_model(model, settings, package, err)
      call system_clock(t1)
      secs_model = real(t1 - t0) / real(rate)

      if (allocated(err)) then
         write(error_unit,'(a)') "fpm-watch: model error: " // err%message
         stop 1
      end if

      call system_clock(t0)
      call targets_from_sources(targets, model, settings%prune, package%library, err)
      call system_clock(t1)
      secs_targets = real(t1 - t0) / real(rate)

      if (allocated(err)) then
         write(error_unit,'(a)') "fpm-watch: targets error: " // err%message
         stop 1
      end if

      call system_clock(tw0)

      allocate(keep(size(targets)))
      keep = .false.

      select type (s => settings)
       type is (fpm_test_settings)
         do i = 1, size(targets)
            if (targets(i)%ptr%is_executable_target(FPM_SCOPE_TEST)) keep(i) = .true.
         end do
       type is (fpm_run_settings)
         if (s%example) then
            call select_names_or_all(targets, FPM_SCOPE_EXAMPLE, s%name, keep)
         else
            call select_names_or_all(targets, FPM_SCOPE_APP, s%name, keep)
         end if
       class default
         keep = .true.
      end select

      nroots = count(keep)

      allocate(visited(size(targets)))
      visited = .false.

      if (is_run_or_test(settings) .and. nroots > 0 .and. nroots <= bit_size(0_int64)) then
         allocate(roots(nroots))
         allocate(target_mask(size(targets)))
         target_mask = 0_int64

         r = 0
         do i = 1, size(targets)
            if (.not. keep(i)) cycle
            r = r + 1
            roots(r)%name = basename(targets(i)%ptr%output_file, suffix=.false.)
            roots(r)%mask = shiftl(1_int64, r-1)
            call dfs_mark_mask(targets(i)%ptr, targets, target_mask, roots(r)%mask)
         end do

         visited = (target_mask /= 0_int64)
      else
         if (allocated(roots)) deallocate(roots)
         allocate(roots(0))
         if (allocated(target_mask)) deallocate(target_mask)
         if (all(keep)) then
            visited = .true.
         else
            do i = 1, size(targets)
               if (keep(i)) call dfs_mark_bool(targets(i)%ptr, targets, visited)
            end do
         end if
      end if

      if (w%watch_deps) then
         call gather_files_with_mask(targets, visited, target_mask, files, file_mask, trim_or_empty(settings%build_dir), dep_dirs)
      else
         call gather_files_with_mask(targets, visited, target_mask, files, file_mask, trim_or_empty(settings%build_dir))
      end if

      if (w%watch_deps) then
         call push_file_with_mask(files, file_mask, "fpm.toml", trim_or_empty(settings%build_dir), 0_int64, dep_dirs)
      else
         call push_file_with_mask(files, file_mask, "fpm.toml", trim_or_empty(settings%build_dir), 0_int64)
      end if

      do i = 1, tree%ndep
         if (.not. allocated(tree%dep(i)%proj_dir)) cycle
         if (len_trim(tree%dep(i)%proj_dir) == 0) cycle

         if (w%watch_deps) then
            call push_file_with_mask(files, file_mask, join_path(tree%dep(i)%proj_dir, "fpm.toml"), trim_or_empty(settings%build_dir), 0_int64, dep_dirs)
         else
            call push_file_with_mask(files, file_mask, join_path(tree%dep(i)%proj_dir, "fpm.toml"), trim_or_empty(settings%build_dir), 0_int64)
         end if
      end do

      n_before = 0
      if (allocated(files)) n_before = size(files)

      call filter_watch_files(files, w%include, w%ignore, file_mask)

      n_after = 0
      if (allocated(files)) n_after = size(files)

      if (w%debug) then
         call log_info(w, "debug: watch files filtered: " // str(n_before) // " -> " // str(n_after))
      end if

      call system_clock(tw1)
      secs_watch = real(tw1 - tw0) / real(rate)

      build_secs = secs_model + secs_targets + secs_watch
      manifest_key = 0_int64

      if (w%verbosity >= 1) then
         call log_info(w, "timings: model=" // ftoa(secs_model) // "s targets=" // ftoa(secs_targets) // "s watchlist=" // ftoa(secs_watch) // "s")
         call log_info(w, "targets: total=" // str(size(targets)) // " closure=" // str(count(visited)))
      end if

      deallocate(package, model)
      if (allocated(targets)) deallocate(targets)
      if (allocated(keep)) deallocate(keep)
      if (allocated(visited)) deallocate(visited)
      if (allocated(target_mask)) deallocate(target_mask)
      if (allocated(dep_dirs)) deallocate(dep_dirs)

   contains

      subroutine collect_dep_dirs(tree, dep_dirs)
         type(dependency_tree_t), intent(in) :: tree
         type(string_t), allocatable, intent(out) :: dep_dirs(:)
         integer :: i

         if (tree%ndep <= 0) then
            allocate(dep_dirs(0))
            return
         end if

         allocate(dep_dirs(tree%ndep))
         do i = 1, tree%ndep
            if (allocated(tree%dep(i)%proj_dir)) then
               dep_dirs(i)%s = normalize_path(trim(tree%dep(i)%proj_dir))
            else
               dep_dirs(i)%s = ""
            end if
         end do
      end subroutine collect_dep_dirs

   end subroutine compute_watch_files_from_settings