sort_target Subroutine

public recursive subroutine sort_target(target, mock)

Topologically sort a target for scheduling by recursing over its dependencies.

Checks disk-cached target digests to determine if targets are up-to-date. Up-to-date targets are tagged as skipped.

On completion, target should either be marked as sorted (target%sorted=.true.) or skipped (target%skip=.true.)

If target is marked as sorted, target%schedule should be an integer greater than zero indicating the region for scheduling

Arguments

Type IntentOptional Attributes Name
type(build_target_t), intent(inout), target :: target
logical, intent(in), optional :: mock

Optionally sort ALL targets if this is a dry run


Source Code

recursive subroutine sort_target(target, mock)
    type(build_target_t), intent(inout), target :: target
    !> Optionally sort ALL targets if this is a dry run
    logical, optional, intent(in) :: mock

    integer :: i, fh, stat
    logical :: dry_run
    
    dry_run = .false.
    if (present(mock)) dry_run = mock

    ! Check if target has already been processed (as a dependency)
    if (target%sorted .or. target%skip) return

    ! Check for a circular dependency
    ! (If target has been touched but not processed)
    if (target%touched) then
        call fpm_stop(1,'(!) Circular dependency found with: '//target%output_file)
    else
        target%touched = .true.  ! Set touched flag
    end if

    ! Load cached target digest if present
    if (.not.allocated(target%digest_cached) .and. &
         exists(target%output_file) .and. &
         exists(target%output_file//'.digest') .and. &
         (.not.dry_run)) then 

        allocate(target%digest_cached)
        open(newunit=fh,file=target%output_file//'.digest',status='old')
        read(fh,*,iostat=stat) target%digest_cached
        close(fh)

        ! Cached digest is not recognized
        if (stat /= 0) deallocate(target%digest_cached)

    end if

    ! Sort dependencies first: the target digest depends on theirs
    do i=1,size(target%dependencies)

        call sort_target(target%dependencies(i)%ptr, dry_run)

    end do

    ! Skip if target was last built from the same inputs: its source file, if any,
    ! and the digests of its dependencies. These catch a dependency rebuilt by an
    ! earlier run that did not rebuild this target (it stopped early, or did not
    ! include it, as `fpm build` does with tests)
    target%digest = target%compute_digest()
    target%skip = .false.
    if (allocated(target%digest_cached) .and. .not.dry_run) then
        target%skip = target%digest_cached == target%digest
    end if

    ! Loop over target dependencies
    target%schedule = 1
    do i=1,size(target%dependencies)

        if (.not.target%dependencies(i)%ptr%skip) then

            ! Can't skip target if any dependency is not skipped
            target%skip = .false.

            ! Set target schedule after all of its dependencies
            target%schedule = max(target%schedule,target%dependencies(i)%ptr%schedule+1)

        end if

    end do

    ! Mark flag as processed: either sorted or skipped
    target%sorted = .not.target%skip

end subroutine sort_target